React 使用Hook。技术知识点说明
2020-5-18 更新说明
- 补充部分
hooks
使用说明。 - 补充功能说明。
说明
在不使用
class
情况下使用state
及其他特性。开始支持版本:React 16.8
使用规则:
- 只能在函数最外层使用
Hook
,不能在循环、条件判断或者子函数中使用。 - 只能在
React
函数组件中使用Hook
。 - 在自定义
Hook
可以使用。 - 没有
this
执行调用;
安装ESline
插件以帮助检测不会犯莫名其妙的错误:
npm install eslint-plugin-react-hooks --save-dev
useState
定义
state
属性。
-
基本使用
说明:
useState
返回一对值:属性变量、设置属性值得方法。useState
接受一个参数作为属性初始值。- 不必定义多个单值;可数组、对象定义,在更新时替换更新;
import React,{useState} from 'react'; function GetStatus(){ const [name,setName] = useState("admin"); const [hobby,setHobby] = useState(["read","write","listen"]); return (<div> <p>{name+":"+props.name}</p> <ul> {hobby.map(item=>(<li key={item.toString()}>{item}</li>))} </ul> </div>); }
-
useState
接受一个函数,函数的参数为当前的属性值;用以新值建立在旧值得逻辑处理。function UserInfo(){ let [info,setInfo] = useState({ name:“admin”, age:25 }); return (<Input onChange={e=>setInfo(prev=>({...prev,age:e.target.value}))} />); }
useEffect
class
中:componentDidMount
/componentDidUpdate
/componentWillUnmount
三个函数的合成共有功能;
不用声明周期区分调用;而以组件功能区分执行调用;
-
基本使用
说明:
- 每次重新渲染都会调用
useEffect
; - 可多次调用
useEffect
以区分注释每个功能、作用; - 如果返回一个函数,将会在组件清除时调用。
- 接受第二个参数
[]
,定义受监控的属性,只有在该属性变化时更新;如果没有指定属性,则再初始时执行一次;
import React,{useState,useEffect} from 'react'; function GetStatus(){ const [name,setName] = useState("admin"); // 会打印两次“doing” useEffect(()=>{ document.title = "Hook"; setTimeout(()=>{ setName("test"); },3000); console.log("doing"); }) // 只会打印一次“doing” useEffect(()=>{ document.title = "Hook"; setTimeout(()=>{ setName("test"); },3000); console.log("doing"); },[]) return (<div> <p>{name}</p> </div>); }
- 每次重新渲染都会调用
-
返回一个函数,在组件销毁时调用,
componentWillUnmount
;function TimeToLog(){ let [time,setTime] = useState(0); let timer = null; useEffect(()=>{ timer = setInterval(()=>setTime(Date().toLocaleString()),2000); return ()=>clearTimeout(timer); }); return(<p>时间:{time}</p>); }
自定义Hook
封装自定义逻辑状态,已达到状态的复用。
说明:
- 每次复用都是独立的
state
,可多次调用。 - 定义规则以
use**
开头。 - 使用场景:表单处理、动画、订阅、计时器。
- 自定义组件在使用
useEffect
时指定依赖项(指定第二个参数)
import React,{useState,useEffect} from 'react';
// 定义在线状态
const statusMap = new Map([[0,"grey"],[1,"green"],[2,"yellow"],[3,"red"]]);
function useOnlineStatus(flag){
const [status,setStatus] = useState(null);
useEffect(()=>{
setStatus(statusMap.get(flag));
},[flag]);
return status;
}
// 在业务组件中使用
function showPersonInfo(props){
const statusStr = useOnlineStatus(props.status);
return (<div>
<p style={{color:statusStr}}>{props.name}</p>
</dvi>);
}
其他Hook说明
来自官网的贴图
useState
- 手动操作对象合并
setPersonObj(preObj=>{ return {...preObj,...newObj}; });
useReducer
适合用于管理包含多个子值得state对象。 - 初始值可传入计算函数;该函数在初始时调用一次
const [name,setName] = useState(()=>nameFormatter(props.name));
useEffect
- 延时调用执行;需要DOM同步更新的变化时
- 接受第二个参数,定义属性值发生变化才会重新渲染
useContext
- 接收一个
context
对象,即<MyContext.Provider>
的value
值,使用props
在组件内获取使用。 - 在
context
发生变化时,组件进行更新渲染;
import React,{useContext} from 'react';
import UserContext from './MyContext';
export default function ChildOne(){
const MyContext = useContext(UserContext);
return (<div>
<p>ChildOne</p>
<span>{MyContext.info.name}</span>
</div>)
}
useReducer
useState
在某些方面的替代方案;- 类似
redux
进行共享数据状态管理;
reducer.js
用于几种处理数据;数据逻辑处理。
function reducer(state,action){
debugger;
switch(action.type){
case "add":
let arr = state.arr;
arr.push(action.name)
return {arr:arr}
case "delete":
state.arr.splice(action.id,1);
return {arr:state.arr}
default:
throw new Error();
}
}
export default reducer;
childOne.js
组件用于展示数据列表。并且进行删除操作。
import React from 'react';
export default function ChildOne(props){
let {data,dispatch} = props;
return (<div>
{data.arr.map((item,index)=><div>
<span>{item}</span>
<span onClick={e=>dispatch({type:"delete",id:index})}>-</span>
</div>)}
</div>)
}
childTwo.js
用于添加数据操作。
import React,{useState} from 'react';
export default function ChildOne(props){
const [name,setName] = useState();
let {dispatch} = props;
return (<div>
<input value={name} onChange={e=>setName(e.target.value)} />
<button onClick={e=>dispatch({type:'add',name:name})}>add</button>
</div>)
}
index.js
作为父组件,传递给两个子组件的state
数据,以及分发事件dispatch
import React,{useReducer} from 'react';
import Reducer from './useReducer';
import ChildOne from "./childOne";
import ChildTwo from './childTwo';
function Index(){
// hook reducer
const [obj,dispatch] = useReducer(Reducer,{arr:[]});
return (<div style={{border:"1px solid #bbb"}}>
<ChildOne data={obj} dispatch={dispatch} />
<ChildTwo dispatch={dispatch} />
</div>);
}
useReducer
可配合useContext
使用。进行跨组件通信、跨组件数据共享。
useCallback
- 返回一个
memoized
函数,接受一个回调函数及依赖项数组作为参数; - 依赖项数组发生变化时,会调用回调函数;
useMemo
- 返回一个
memoized
函数,接受一个回调函数及依赖项数组作为参数;
useRef
- 返回一个可变的
ref
对象,在整个组件声明周期中保持不变;
useImperativeHandle
- 在使用
ref
时自定义暴露给父组件的实例值
useLayoutEffect
- 在重新渲染时同步更新DOM;
useDebugValue
- 用于在
React
开发工具中显示自定义Hook的标签。
使用Hooks
的疑问难点
- 父组件获取子组件实例的数据(通过调用子组件的方法获取)
// 父组件 function ParentInfo(){ let childRef = null; function handleGetInfo(e){ e.stopPropagation(); let obj = childRef().getInfo(); console.log(obj); } return (<div> <button onClick={handleGetInfo}>获取</button> <ChildInfo onRef={ref=>childRef=ref} /> </div>); } // 子组件实例 function ChildInfo(props){ let [name,setName] = useState("admin"); useEffect(()=>{ let {onRef} = props; onRef(()=>{ return { getInfo:getInfo } }); }); function getInfo(){ return { name:name } } return (<input value={name} onChange={e=>setName(e.target.value)} />); }