Hook
Hook是React16.8的新增特性,它可以让你在不编写class的情况下使用state以及其他的React特性
使用Hook的原因
- Hook使你在无需修改组件结构的情况下复用状态逻辑
- Hook将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
- Hook使你更在非class的情况下可以使用更多的React特性,从概念上讲,React组件一直更像是函数。而Hook则拥抱了函数,同时也没有牺牲React的精神原则。Hook提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术
State Hook
useState就是一个Hook,通过在函数组件里调用它来给组件添加一些内部state。React会在重复渲染时保留这个state。useState会返回一对值:当前状态和一个让你更新它的函数。你可以在事件处理函数中或其他一些地方调用这个函数。它类似class组件的this.setState,但是它不会把新的state和旧的state进行合并。useSate唯一的参数就是初始state,这个初始state参数只有在第一次渲染时会被用到。
声明多个state变量
你可以在一个组件中多次使用State Hook
function ExampleWithManyStates(){
// 声明多个state变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{text:'Learn Hooks'}])
}
Effect Hook
在React组件中执行数据过滤、订阅或者手动修改过DOM,我们统一把这些操作称为“副作用”或者简称为“作用”
useEffect 就是一个Effect Hook,给函数组件增加了操作副作用的能力。它跟class组件中的componentDidMount、componentDidUpdate和componentWillUnmount具有相同的用途,只不过被合并成了一个API。
当你调用useEffect时,就是在告诉React在完成对DOM的更改后运行你的“副作用”函数,由于副作用函数是在组件内声明的,所以它们可以访问到组件的props和state,默认情况下,React会在每次渲染后调用副作用函数——包括第一次渲染的时候。
副作用函数还可以通过返回一个函数来指定如何“清除”副作用,例如,在组件中使用副作用函数来订阅好友的在线状态,并通过取消订阅来进行清除操作。
Hook 使用规则
Hook就是JavaScript函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用Hook。不要在循环、条件判断或者函数中调用
- 只能在React的函数组件中调用Hook或自定义Hook中。不要在其他JavaScript函数中调用
自定义Hook
实现组件之间状态逻辑之间的复用,目前有两种主流方案来解决这个问题:高阶组件和render props。自定义Hook可以让你在不断增加组件的情况下达到同样的目的。
其他Hook
除此之外还有一些使用频率较低的但是很有用的Hook。
比如useContext让你不是用组件嵌套就可以订阅React的Context。
另外useReducer可以让你通过reducer来管理组件本地的复杂state。
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init)
useState的替代方案,它接收一个形如(state,action)=> newState的reducer,并返回当前的state以及与其配套的dispatch方法,在某些场景下,useReducer会比useState更适用,例如state逻辑较复杂且包含多个子值,或者下一个state依赖于之前的state等。并且,使用useReducer还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递dispatch而不是回调函数。
useRef
const refContainer = useRef(initialValue)
useRef返回一个可变的ref对象,其.current属性被初始化为传入的参数(initialValue),返回的ref对象在组件的整个生命周期内保持不变。
useImperativeHandle
useImperativeHandle(ref,createHandle,[deps])
useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用ref这样的命令式代码。useImperativeHandle应当与forwardRef一起使用:
function FancyInput(props,ref){
const inputRef = useRef();
useImperativeHandle(ref,()=>({
focus:()=>{
inputRef.current.focus();
}
}))
return <input ref={inputRef} ... ></input>
}
FancyInput = forwardRef(FancyInput)
在上述例子中,渲染< FancyInput ref = {inputRef} />的父组件可以调用inputRef.current.focus()。
useLayoutEffect
其函数签名与useEffect相同,但它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect内部的更新计划将被同步刷新。
尽可能使用标准的useEffect以避免阻塞视觉更新。