Reselect的createSelector搭配EntityAdapter的selectAll缓存失效问题
Redux Toolkit createEntityAdapter selectAll缓存失效问题解答
问题原因
你遇到的selectAll每次返回新引用是createEntityAdapter的正常设计行为,不是操作错误、环境问题或版本bug:createEntityAdapter内置的所有选择器本身是基于reselect的记忆化选择器,不同选择器的依赖逻辑不同:
selectIds的依赖是切片下的ids数组,只有执行增/删实体的操作(addOne/removeOne/setAll等)时ids数组引用才会更新,修改单条实体的updateOne操作不会修改ids,因此你基于它封装的选择器缓存可以正常生效selectAll的依赖是ids数组 +entities对象。调用updateOne时,RTK基于Immer做不可变更新,被修改的实体引用会变化,整个entities对象的引用也会同步更新,因此selectAll的依赖触发重计算,每次都会返回新的数组实例,导致基于它封装的自定义选择器缓存失效。
性能优化方案
你要实现「单条todo更新不重渲染整个列表」的需求,不要用selectAll传递全量数据给列表组件,按以下方式实现即可:
- 列表父组件只订阅
selectTodosIDs获取id数组:
const todoIds = useSelector(selectTodosIDs)
只要没有todo的增删操作,todoIds引用不会变化,父组件不会触发重渲染。
2. 每个Todo子组件单独接收id作为prop,用selectTodoById订阅自身对应的数据:
// 列表组件渲染逻辑 {todoIds.map(id => <TodoItem key={id} todoId={id} />)} // TodoItem组件内部 const TodoItem = ({todoId}) => { const todo = useSelector(state => selectTodoById(state, todoId)) // 其他逻辑 }
此时只有被修改的todo对应的子组件会触发重渲染,其余子组件完全不受影响,可达到最优性能。
自定义全量列表选择器优化
如果你确实需要基于全量todo列表做计算(比如筛选、聚合统计等),不想每次修改单条数据都触发重计算,可以自定义记忆化逻辑:
使用createSelectorCreator自定义比较函数,对selectAll返回的数组做浅比较,只有数组内部元素的引用变更符合你的预期时才触发重计算:
import { createSelectorCreator, shallowEqual } from '@reduxjs/toolkit' const createShallowSelector = createSelectorCreator({ memoize: shallowEqual }) export const selectTodosCustom = createShallowSelector( selectAll, (todos) => { console.log('execute output function') // 你的计算逻辑 } )
内容的提问来源于stack exchange,提问作者foralobo
相关产品推荐
相关产品推荐

