什么时候自定义Hook?
当多个组件之间有一些公共的逻辑,可以将他们抽取成自定义的Hook,Hook本质是自定义的函数。
自定义Hook的一些tips
- 自定义的hook必须以use作为开头,因为只有以use开头的函数,react才能将其识别为hook,这样react才可以识别出来该函数是自定义的Hook,还是普通函数。因为普通函数里面不能调用其他hook,否则会报错。
- 基于tip1,所以在自定义的hook中可以调用其他Hook。
- 在两个不同的组件A和B中调用相同的hook,组件A和B不会共享状态,两个hook的状态是隔离的。
使用单个还是多个state变量
有时候在写代码的时候,为了避免声明多个useState hook,我们会将多个变量合成一个对象,声明一个useState hook,示例如下,那这两种方式有区别吗?
const [a, setA] = useState(1);
const [b, setB] = useState(2);
const [c, setC] = useState({a:1, b:2});
两种方式都行,适用场景不同:
- 基于state间业务相关性,可以将多个state合成一个对象,好处是可读性强一些。如果出现state数量较多,逻辑比较复杂,可以考虑使用reducer。
- 采用多个变量合成一个对象方式,缺点是:如果其中某一个变量更新,需要更新整个对象,不能漏掉任何一个未更新的变量,因为react不会自动合并对象的更新。
- 采用单个变量,考虑到扩展性,如果后期出现多个组件之间有重复的逻辑,需要提取自定义的Hook时,很容易抽取一个自定义的Hook。
在使用了hook的函数组件中如何获取上次的props和state
目前react官方还没有提供这样的hooks,未来可能会有
可以使用useRef来实现,代码如下:
const calculator = () => {
const [total, setTotal] = useState(0);
const prevTotal = usePrevious(total);
return (
<div>以前的总数:<span>{prevTotal}</span>
现在的总数:<span>{total}</span>
</div>
)
}
const usePrevious = (value) => {
const ref = useRef()
useEffect(()=>{
ref.current = value
})
return ref.current
}
为什么使用ref能保存上次state的值?
简单介绍useRef的用法,useRef返回一个ref对象,他在组件的整个证明周期都保持不变,都是同一个ref对象。意思就是ref.current的值只要没有被代码改变,无论组件重新渲染多少次,ref.current的值都不变。
因为useEffect是componentDidMount, componentDidUpdate和componentWillUnmount三个生命周期函数的组合,所以useEffect是在DOM第一次渲染和DOM每次更新完成后执行。
执行顺序如下:
函数组件被调用->执行代码->渲染DOM->执行useEffect->
- calculator组件初次渲染;此时ref.current的值为undifined,因为useEffect还没有执行,所以prevTotal 为undifined, total为0; DOM渲染结束;执行useEffect,此时ref.current=0;
- total值变为1, state发生变化,calculator组件再次渲染,此时ref.current = 1; 因为useEffect还没有执行,所以prevTotal 为0, total为1;
所以ref.current能精准的保存上一次state的值。
一些性能优化的场景
- 惰性初始化sate,如果state的初始值是通过复杂的计算得到,则可以给useState传递一个函数,这样state的初始值计算只会在首次渲染执行,避免多次渲染多次执行state初始化函数。代码如下:
const calculator = () => {
const [total, setTotal] = useState(() => {getInitialTotal()} );
}
2.使用useMemo hook去进行一些复杂的计算
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
只有当依赖的数组值发生改变时,才会再次执行相关的函数,这样避免了每次渲染都进行一些高开销的运算,达到了性能优化。
useMemo是在DOM渲染期间执行。
- 使用React.memo(), 可以防止不必要地重新渲染函数组件。它是对传递给组件的props的浅比较,如果 props 没有改变,那么组件将不会重新渲染。