Hook 是 React 16.8 开始支持的新特性,旨在用函数式组件代替类式组件。本文记录常用Hook用法及注意点
1、useState
返回一个 state,以及更新 state 的函数。给函数式组件增加内部状态,状态一旦变化,组件会重新渲染
const [state, setState] = useState(initialState);
- useState 可以接受单值或函数,若是函数,则以函数返回值作为初始state,且该函数仅在初始化时执行一次,后期组件更新不再执行
- setState 可以接受单值或函数,若是函数,则函数第一个参数是上一个state值,返回值若与上一个state值一样,则组件不重新渲染
- 与类式组件的setState不同,函数组件的 setState 不会合并所有状态,不过可以这样做到:
setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
- 使用 Object.is方法判断前后 state 是否相等
2、useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
当传入第三个函数参数,则state=init(initialArg);否则 state = initialArg
- useReducer 是 useState 的替代方案
- reducer: (state, action) => newState
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
3、useEffect
接收一个包含命令式、且可能有副作用代码的函数A和一个依赖数组B作为参数。函数A会在组件渲染到屏幕之后执行
。每次重新渲染组件时,B中的值不变化,则函数A不会重新执行
useEffect(()=>{
// 副作用代码,如订阅、dom操作、定时器、获取数据等
},[]);
- 函数A可以返回一个函数,用于清除副作用,如取消订阅、定时器等操作
- 如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除
- 函数A会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。因此不应在函数A中执行阻塞浏览器更新屏幕的操作,不然用户会感觉到视觉上的不一致
4、useLayoutEffect
函数签名与 useEffect 相同,会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
- useEffect 是异步的,useLayoutEffect 是同步的
5、useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback 返回第一个函数A参数的记忆化版本,该函数A的引用只有在依赖数组的元素发生改变时才会改变。useCallback本身会在每次重新渲染时执行
6、useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
返回一个记忆化的值V,该值只有在依赖数组的元素发生改变时才会改变。useCallback本身会在每次重新渲染时执行。
- useCallBack 用于记忆函数,useMemo 用于记忆需要复杂计算而来的衍生值
- useMemo 的第一个函数参数在初次渲染时也会执行
7、useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。
// const refContainer = useRef(initialValue);
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
- ref 对象的 current 属性值是可变的,可保存任何值
- ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。
- 回调 ref
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
8、自定义 Hook
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
- 用 useXXX 命名
- 通常内部调用 useState,useEffect 等内置hook
- 返回值随意
网友评论