Redux createSelector未实现结果记忆化,函数异常触发原因咨询
问题根因排查
- 首先明确
createSelector的默认缓存规则:仅当输入参数的**引用完全相等(===比较)**时才会命中缓存,默认缓存大小为1,仅记住上一次的输入输出。 - 你当前的写法首先存在不必要的封装:你定义的两个
createSelector没有任何计算逻辑,只是透传state中的字段,和直接写普通selector函数效果完全一致,不会带来额外的缓存收益。 - 注意确保你的
createSelector是全局定义的,不要写在组件内部,否则每次组件渲染都会生成新的selector实例,缓存完全独立,自然无法命中。 - 如果是组件卸载后重新进入触发的重新计算,要注意组件内部的
useMemo缓存会随组件销毁清空,但是全局定义的selector缓存不会丢失,只要store中对应字段引用不变,useAppSelector取到的值会直接命中selector缓存。
最可能的触发原因
你每次更新store中rawTranscript字段时,即使内容完全相同,也生成了新的引用:
比如reducer中存在无意义的解构赋值rawTranscript: [...oldRaw],或者接口返回数据时直接将新的数组/对象赋值给该字段,即使内容和之前完全一致,引用地址已经发生变化,自然会触发createSelector重新计算、useMemo依赖更新、副作用执行。
排查方案
在组件中加入日志对比两次获取的rawTranscript是否全等:
const lastRaw = useRef() const rawTranscript = useAppSelector(selectRawTranscript); // 多次进入组件时打印,返回false则说明引用发生了变化 console.log(rawTranscript === lastRaw.current) lastRaw.current = rawTranscript
如果返回false,说明问题出在store更新逻辑,而非selector用法错误。
解决方案
方案1:优化store更新逻辑(优先推荐)
仅当rawTranscript内容真正发生变化时,才更新该字段的引用,不变的情况下直接复用旧的引用即可,即可天然命中默认的引用比较缓存。
方案2:自定义selector的比较规则
如果无法控制store层的引用更新(比如接口每次都返回新的对象),可以自定义支持深比较的selector:
import { createSelectorCreator, defaultMemoize } from '@reduxjs/toolkit'; import { isEqual } from 'lodash'; // 创建深比较的selector生成器 const createDeepEqualSelector = createSelectorCreator( defaultMemoize, isEqual ); // 改写你的selector,输入参数会进行深比较,内容相同就会命中缓存 export const selectRawTranscript = createDeepEqualSelector( (state: RootState) => state.transcript.rawTranscript, rawTranscript => rawTranscript );
注意:深比较会带来额外的性能开销,如果数据量较大需要权衡收益。
代码优化建议
你当前的组件逻辑可以进一步简化,不需要在组件内做解析+dispatch更新store的操作,直接把解析逻辑封装到selector中即可:
// slice中直接定义解析后的selector export const selectParsedTranscript = createSelector( (state: RootState) => state.transcript.rawTranscript, (rawTranscript) => transcriptParser(rawTranscript, undefined, 0.9) )
组件中直接取值即可,省去useMemo、useEffect的冗余代码:
const parsedTranscript = useAppSelector(selectParsedTranscript);
内容的提问来源于stack exchange,提问作者kevin
相关产品推荐
相关产品推荐

