React中useState函数式更新与useCallback结合避免重渲染原理解疑
两种写法的重渲染差异核心逻辑
阻止子组件重渲染是 useCallback 空依赖数组 + useState 函数式更新共同作用的结果,二者缺一不可。
情况1会触发重渲染的原因
第一种写法的代码如下:
const onRemove = useCallback( tickerToRemove => { setWatchlist(watchlist.filter(ticker => ticker !== tickerToRemove)); }, [watchlist] );
这里 useCallback 的依赖数组包含状态 watchlist:
- 每次
watchlist发生变更时,useCallback都会生成一个全新的onRemove函数引用 - 只要这个函数作为 props 传给子组件,哪怕子组件用
React.memo包裹,props 引用变化也会触发子组件重渲染 - 这种写法不能把
watchlist移出依赖数组:如果移出会触发闭包陷阱,onRemove拿到的永远是第一次渲染的旧watchlist值,过滤逻辑会直接出错。
情况2可以避免重渲染的原因
第二种写法的代码如下:
const onRemove = useCallback(tickerToRemove => { setWatchlist(watchlist => watchlist.filter(ticker => ticker !== tickerToRemove) ); }, []);
这里函数式更新的作用是让状态更新逻辑不依赖外层作用域的状态值:
- 传给
setWatchlist的回调函数,接收的参数是 React 内部保证的最新watchlist状态,不需要从外层作用域获取,因此onRemove的执行逻辑完全不依赖外部的watchlist变量 - 因此可以给
useCallback传入空依赖数组,这样onRemove的函数引用从组件首次挂载后就永远不会变化 - 不变的 props 传给
React.memo包裹的子组件,就不会触发不必要的子组件重渲染。
二者缺一不可的验证
- 只使用函数式 setter 但没有给
useCallback正确设置空依赖:函数引用还是会随依赖变化,子组件依旧会重渲染 - 只给
useCallback设置空依赖但不使用函数式 setter:会触发闭包陷阱,拿到的状态永远是旧值,业务逻辑直接出错,谈不上重渲染优化。
内容的提问来源于stack exchange,提问作者copenndthagen
相关产品推荐
相关产品推荐

