shallowEqual对createEntityAdapter生成的选择器无效问题排查
问题:使用shallowEqual后仍出现Redux选择器引用变化警告
我们代码库中有如下Redux Toolkit实体适配器相关的选择器实现:
import { createEntityAdapter } from '@reduxjs/toolkit' export const itemsAdapter = createEntityAdapter({ selectId: item => item.itemId }) export const itemsSelectors = itemsAdapter.getSelectors(state => state.items) export const selectItemsById = (state, itemId) => itemsSelectors.selectById(state, itemId) export const selectLotsOfItems = state => { const item1 = selectItemsById(state, '1') const item2 = selectItemsById(state, '2') const item3 = selectItemsById(state, '3') const item4 = selectItemsById(state, '4') return [item1, item2, item3, item4] }
在组件中调用该选择器时:
const lotsOfItems = useSelector(state => selectLotsOfItems(state))
控制台弹出警告,提示选择器每次返回新的数组引用会引发不必要的组件重渲染。尝试添加shallowEqual作为useSelector的第二个参数:
import { useSelector, shallowEqual } from 'react-redux' const lotsOfItems = useSelector(state => selectLotsOfItems(state), shallowEqual)
但警告依然存在,请问这是什么原因?
原因解析
- shallowEqual的比较逻辑:
shallowEqual会先对比两个值的引用是否一致。selectLotsOfItems每次调用都会创建全新的数组实例,哪怕数组内元素的引用和之前完全相同,数组本身的引用是新的,shallowEqual会直接判定值发生变化,进而触发重渲染,同时保留原有的警告。 - Redux开发工具的检测逻辑:工具警告的依据是选择器返回值的引用稳定性,不管有没有加
shallowEqual,只要选择器每次返回新引用,警告就会持续触发——它检测的是返回值引用的频繁变化,而非最终是否真的触发了组件重渲染。
解决办法
要彻底消除警告,得让选择器返回稳定的引用,推荐用Redux Toolkit自带的createSelector做记忆化处理:
import { createEntityAdapter, createSelector } from '@reduxjs/toolkit' // 原有适配器和基础选择器代码不变 export const itemsAdapter = createEntityAdapter({ selectId: item => item.itemId }) export const itemsSelectors = itemsAdapter.getSelectors(state => state.items) export const selectItemsById = (state, itemId) => itemsSelectors.selectById(state, itemId) // 使用createSelector记忆化选择器 export const selectLotsOfItems = createSelector( // 输入依赖:获取需要的单个item [ state => selectItemsById(state, '1'), state => selectItemsById(state, '2'), state => selectItemsById(state, '3'), state => selectItemsById(state, '4') ], // 结果函数:只有当输入的item引用变化时,才会生成新数组 (item1, item2, item3, item4) => [item1, item2, item3, item4] )
之后在组件里直接用const lotsOfItems = useSelector(selectLotsOfItems)就行,不用再加shallowEqual。因为createSelector会在输入依赖没变化时,返回之前缓存的数组引用,既消除了警告,也避免了不必要的组件重渲染。
内容的提问来源于stack exchange,提问作者noblerare
相关产品推荐
相关产品推荐

