You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

但警告依然存在,请问这是什么原因?


原因解析

  1. shallowEqual的比较逻辑:shallowEqual会先对比两个值的引用是否一致。selectLotsOfItems每次调用都会创建全新的数组实例,哪怕数组内元素的引用和之前完全相同,数组本身的引用是新的,shallowEqual会直接判定值发生变化,进而触发重渲染,同时保留原有的警告。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:55:14