何时需要在useSelector()中使用自定义相等比较函数?
问题核心结论
你观察到的现象完全符合useSelector的设计逻辑,不存在对文档的误解,核心差异在于选择器是否每次执行都创建新的对象引用。useSelector默认使用严格相等===做前后返回值的比较,你手动传入的(a, b) => a === b和默认比较逻辑完全一致。
你写的state => state.data是直接返回Redux store中已经存在的对象引用:Redux(包括Redux Toolkit)强制遵循不可变更新规则,只有当state.data对应的状态切片被实际修改时,它的引用才会被替换为新的对象;未修改时前后两次拿到的是同一个内存地址的对象,===比较自然返回true,不会触发多余重渲染。
文档描述的「返回新对象强制重渲染」对应的场景
文档提到的默认重渲染逻辑,仅针对选择器函数每次执行时主动创建新的非原始值引用的情况,常见场景包括:
- 选择器内手动拼接多个状态字段返回新对象
// 每次执行都会生成全新的对象实例,哪怕a、b的值完全没变,新对象的引用和旧对象不同,===比较返回false,会触发重渲染 const data = useSelector(state => ({ a: state.data.a, b: state.data.b }))
- 选择器内对数组做过滤、映射等返回新数组的操作
// 每次执行都返回新的数组实例,哪怕过滤后的元素和之前完全一致,新数组引用不同就会触发重渲染 const visibleList = useSelector(state => state.list.filter(item => item.isVisible))
- 选择器内派生生成新的非原始值,比如新的日期实例、正则实例等
// 每次执行都new新的日期对象,引用永远不相等,会持续触发重渲染 const formatDate = useSelector(state => new Date(state.currentTimestamp))
自定义相等函数的适用场景
当你的选择器确实需要返回新生成的非原始值,且不希望仅因引用变化就触发重渲染,而是要等返回值的实际内容变化时才重渲染,就需要传入自定义相等函数,使用时遵循以下优先级即可:
- 优先拆分多个
useSelector调用,每个选择器只返回原始类型值(字符串、数字、布尔值等),这种场景默认===比较完全够用,性能最高。 - 需要返回对象/数组等非原始值时,优先使用记忆化选择器(Redux Toolkit内置的
createSelector即可实现),只要依赖的状态值没变化,记忆化选择器会直接返回上一次的结果引用,默认===比较就可以正常工作,不需要额外的比较逻辑。 - 无法使用记忆化,且对象结构比较扁平、比较成本低时,可以使用浅比较函数(比如React-Redux内置的
shallowEqual)作为自定义比较逻辑。 - 只有当返回值结构简单、字段数量少,且确实需要深度对比每一个字段值的时候,才考虑使用类似
isEqual的深比较函数。深比较的性能开销会随对象嵌套层级、字段数量上升而增长,不适合全场景滥用。
内容的提问来源于stack exchange,提问作者Arkellys
相关产品推荐
相关产品推荐

