useMemo与useCallback未减少渲染次数,正确用法是什么?
首先得点破核心问题:你虽然用了useCallback缓存了increment1和increment2的函数引用,但你的子组件CountBtn1和CountBtn2并没有做渲染优化。当父组件Counter因为count1更新而重新渲染时,默认情况下所有子组件都会跟着重新渲染,不管它们的props有没有变化。
先解决你的当前问题:让useCallback生效
要让useCallback发挥减少子组件渲染的作用,你需要用React.memo包裹你的子组件。React.memo是一个高阶组件,它会对比子组件前后的props,如果props没有变化,就跳过重新渲染。
修改你的代码试试:
// 用React.memo包裹子组件 const CountBtn1 = React.memo(function({ onClick, count }) { console.log('CountBtn1 render') return <button onClick={onClick}>{count}</button> }) const CountBtn2 = React.memo(function({ onClick, count }) { console.log('CountBtn2 render') return <button onClick={onClick}>{count}</button> }) function Counter() { const [count1, setCount1] = React.useState(0) // useCallback缓存函数,依赖为空数组,所以每次渲染都是同一个引用 const increment1 = React.useCallback(() => setCount1(c => c + 1), []) const [count2, setCount2] = React.useState(0) const increment2 = React.useCallback(() => setCount2(c => c + 1), []) return ( <> <CountBtn1 count={count1} onClick={increment1} /> <CountBtn2 count={count2} onClick={increment2} /> </> ) }
现在你再点击CountBtn1,控制台只会输出CountBtn1 render,CountBtn2不会跟着渲染了——因为它的props(count2和increment2)都没变化,React.memo帮它跳过了渲染。
useCallback的本质是什么?
useCallback的作用是缓存函数的引用,避免每次父组件渲染时都创建一个新的函数实例。如果不用useCallback,每次父组件渲染都会生成新的increment1和increment2,就算你用了React.memo,因为函数引用变了,子组件还是会重新渲染。所以useCallback和React.memo是一对好搭档,要一起用才能发挥作用。
那useMemo呢?正确用法是什么?
useMemo和useCallback有点像,但它是用来缓存计算结果的,而不是函数引用。当你有一个需要大量计算的操作(比如复杂的数组过滤、排序,或者递归计算),每次渲染都重新计算会浪费性能,这时候就用useMemo把结果缓存起来,只有当依赖项变化时才重新计算。
举个例子:
function ExpensiveComponent({ list }) { // 用useMemo缓存过滤后的列表,只有list变化时才重新过滤 const filteredList = React.useMemo(() => { console.log('重新过滤列表') return list.filter(item => item.value > 100) }, [list]) return <div>{filteredList.map(item => <p key={item.id}>{item.name}</p>)}</div> }
这里如果不用useMemo,每次组件渲染都会执行list.filter,就算list没有变化也会重复计算。用了useMemo后,只有当list变化时,才会重新执行过滤逻辑。
注意:不要滥用useCallback和useMemo
这两个钩子都是用来做性能优化的,但不是所有场景都需要用。缓存本身也有开销,比如需要存储依赖项、对比依赖项变化。如果你的组件渲染速度很快,没有明显的性能问题,强行用这些钩子反而会增加不必要的复杂度。只有当你确实观察到组件因为重复渲染或者重复计算导致性能下降时,再考虑使用它们。
内容的提问来源于stack exchange,提问作者951565664

