如何用Reselect选择器避免Redux Store高频变动属性触发重计算
解决方案
方案1:自定义Selector比较逻辑(改造成本最低)
reselect默认使用引用相等判断输入是否变化,你可以通过自定义比较规则忽略position字段的变动,不需要修改原有Store结构和业务逻辑:
import { createSelectorCreator, defaultMemoize } from 'reselect' import isEqualWith from 'lodash/isEqualWith' // 自定义相等判断:所有字段都相等才算相等,position字段直接判定为相等 const ignorePositionComparer = (prevVal, nextVal) => { return isEqualWith(prevVal, nextVal, (a, b, key) => { if (key === 'position') return true }) } // 生成带自定义比较规则的selector创建器 const createCustomSelector = createSelectorCreator( defaultMemoize, ignorePositionComparer ) // 直接替换原有createSelector即可 const getSelectedItems = createCustomSelector( getAllItems, getSelectedItemsKeys, (allItems, selectedKeys) => allItems.subset(selectedKeys) )
如果不想引入lodash,也可以自己实现对应字段的比较逻辑,只检查你关心的uivalue1、uivalue2等UI字段是否变化即可。
方案2:拆分输入选择器,仅依赖UI相关字段
从根源上避免把包含position的完整allItems作为输入依赖,只提取你需要的UI字段作为selector的输入源:
// 提取单个item的UI相关数据,完全不包含position const getSelectedItemsUiData = createSelector( getAllItems, getSelectedItemsKeys, (allItems, keys) => keys.map(key => ({ key, uivalue1: allItems[key].uivalue1, uivalue2: allItems[key].uivalue2 })) ) // 后续UI组件直接依赖这个selector即可,position变动不会触发它重新计算 const getSelectedItemsForUi = createSelector( getSelectedItemsUiData, (uiDataList) => uiDataList )
如果你的组件只用到UI字段,直接用这个selector返回的结果就行,不需要拿完整的item数据。
方案3:修复re-reselect的使用逻辑
你之前的用法确实是对re-reselect的理解有偏差:它的缓存key仅用于多实例缓存,只要输入选择器的返回值发生变化,不管缓存key是否相同,都会重新执行计算逻辑。如果要继续用re-reselect,需要增加一个和position无关的hash输入:
// 生成仅和UI字段相关的哈希,position变动不会改变哈希值 const getSelectedItemsUiHash = createSelector( getAllItems, getSelectedItemsKeys, (allItems, keys) => keys.map(key => `${key}_${allItems[key].uivalue1}_${allItems[key].uivalue2}` ).join('|') ) const getSelectedItems = createCachedSelector( getSelectedItemsUiHash, getSelectedItemsKeys, getAllItems, (hash, keys, allItems) => allItems.subset(keys) )( (state) => `keys-${state.selectedItemsKeys.join(':')}` )
这样只有getSelectedItemsUiHash返回的哈希值变化时,才会重新执行subset计算,position变动不会触发重计算。
内容的提问来源于stack exchange,提问作者mkhatib
相关产品推荐
相关产品推荐

