React Hooks中useCallback和useMemo的作用
useCallback
useCallback
会返回一个 memoized 的函数。看起来它是用来对函数进行缓存,防止总是重复的生成新的函数。
对函数缓存,这到底有什么用处呢?
export function Demo() {
const [value, setValue] = useState(0);
const changeValue = () => {
setValue(value + 1);
};
return (
<div>
{/*SomeComplexComponent是一个十分复杂的组件*/}
<SomeComplexComponent onIncrement={changeValue} />
</div>
);
}
假如SomeComplexComponent
是一个十分复杂的组件,如果changeValue
变化了,势必会导致SomeComplexComponent
这个组件重新渲染,这就带来了额外的性能浪费,因为前后这个组件并没有发生变化。如果使用class
风格的 React,可以在shouldComponentUpdate
进行判断,避免不必要渲染,但是,在这里的函数中不存在shouldComponentUpdate
钩子,还好,React 提供了useCallback
这个 hook,借助这个 hook 也可以做到shouldComponentUpdate
同样的操作。使用useCallback
来对changeValue
进行缓存,保证每次都返回是同一个函数,就避免了SomeComplexComponent
组件的重新渲染。
既然使用useCallback
能对函数进行缓存操作,我们怎么去验证每次的函数都是相同的呢?
可以使用Set
将每次生成的函数保存起来,看最终Set
的长度,如果一直是1
就能说明每次的函数都是同一个函数。
const set = new Set();
export function Demo() {
const [value, setValue] = useState(0);
const changeValue = useCallback(() => {
setValue(value + 1);
}, []);
const [count, setCount] = useState(0);
set.add(changeValue);
console.log(set.size);
return (
<div>
{/*SomeComplexComponent是一个十分复杂的组件*/}
<SomeComplexComponent onIncrement={changeValue} />
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
);
}
这里用count
触发重新渲染,结果在控制台看到打印的长度始终是1
。如果把useCallback
去除,你会看到长度会进行递增。就是说,useCallback
确实使函数返回的都是同一个,既然值没有变化,就不会导致SomeComplexComponent
重新渲染。
但是,上述示例还是有 bug,对代码做一些修改,点击按钮对value
进行递增操作。结果,value
的值始终是1
,并没有继续递增了。
export function Demo() {
const [value, setValue] = useState(0);
const changeValue = useCallback(() => {
setValue(value + 1);
}, []);
return (
<div>
{/*SomeComplexComponent是一个十分复杂的组件*/}
<SomeComplexComponent onIncrement={changeValue} />
{value}
<button onClick={changeValue}>count++</button>
</div>
);
}
可以看到value
的值始终为1
。这是什么情况?
这是因为useCallback
的第二个参数没有指定,它只会在第一次时进行changeValue
闭包缓存,所以初始是0
,缓存后就一直为0
,执行一次自增,所以值始终是1
。
解决办法很简单,指定useCallback
的依赖即可,当依赖发生变化时,其自动更新缓存。
const changeValue = useCallback(
() => {
setValue(value + 1);
},
[value]
);
useMemo
这个 hook 表示对函数求值进行缓存,当依赖没有发生变化时,不执行计算,直接返回缓存结果(有点像 vue 的计算属性)。对于一些复杂耗时的计算,使用它能够优化程序的性能。
使用起来和useCallback
类似,只不过useMemo
返回的是一个值。
const newValue = useMemo(() => Math.sqrt(value), [value]);
总结
这两个 hook 的设计目的都是用来缓存操作的。useCallback
用来缓存函数,useMemo
用来缓存计算结果。当你的程序出现一些重复渲染和频繁计算操作时,就可以考虑用这两个 hook 来优化了。
- 本博客所有文章除特别声明外,均可转载和分享,转载请注明出处!
- 本文地址:https://www.leevii.com/?p=2385