如何让useCallback与Redux-Toolkit createSelector记忆化正常生效?
问题根源
你的代码记忆化不生效的核心错误是:useCallback(selectSum, []) 只是缓存了全局导入的同一个selectSum引用,两个Sum组件实例调用的始终是全局共享的同一个selector。
当两个组件分别传入value=1、value=2调用同一个selector时,selector每次收到的第二个参数都在两个值之间来回切换,永远无法命中记忆缓存,因此每次重渲染都会触发两次重新计算、打印两次日志。
修复方法
官方文档要求的「每个组件创建独立selector实例」,指的是每个组件实例需要拥有专属的全新记忆化selector,而非复用全局唯一的selector。具体修改分两步:
- 第一步:把全局导出的固定selector,改成可以生成新selector实例的工厂函数
- 第二步:在组件内通过
useMemo调用工厂函数,为当前组件生成专属selector,不要用useCallback包裹全局selector
修改reducer侧的selector定义
// 移除原来全局固定的selectSum导出 // export const selectSum = createSelector(...) // 改为导出selector工厂函数,每次调用都会返回独立的新selector实例 export const makeSelectSum = () => createSelector( [state => state.baseNum, (_, addNum) => addNum], (baseNum, addNum) => { console.log('触发sum计算') return baseNum + 100 + addNum } )
修改组件侧的调用逻辑
import { useMemo } from "react"; import { useSelector, useDispatch } from "react-redux"; import { increment, makeSelectSum } from "./reducer"; function Sum({ value }) { // 给当前组件实例生成独一份的selector,组件挂载到卸载期间引用不变 const selectSumForThisInstance = useMemo(makeSelectSum, []); // 调用专属selector传参,每个组件的selector只会收到固定的value值 const sum = useSelector(state => selectSumForThisInstance(state, value)); return ( <div> sum: {sum} (10+100+{value}) </div> ); } export default function App() { const dispatch = useDispatch(); return ( <div> <Sum value={1} /> <Sum value={2} /> <button onClick={() => dispatch(increment())}> change state and re-render </button> </div> ); }
修复后的效果
- 首次页面加载:两个组件各自初始化自己的selector,各计算一次,共打印2次计算日志
- 后续点击按钮触发重渲染:只要两个组件的
valueprop不发生变化,两个selector都会命中缓存,不会触发重新计算,没有额外日志输出 - 如果某个组件的
valueprop更新:只会触发对应组件自身的selector重新计算,不会干扰其他组件的缓存结果
注意:
useCallback只能缓存已有函数的引用,不会创建新的selector实例,所以用它包裹全局selector完全达不到「每个组件独立实例」的要求,必须用工厂函数配合useMemo生成新实例才行。
内容的提问来源于stack exchange,提问作者Chong Lip Phang
相关产品推荐
相关产品推荐

