使用Reselect的createSelector在Redux Store中选择关联数据
Redux + Reselect 嵌套选择器的优化建议
我看你现在用Redux搭着Reselect的createSelector在mapStateToProps里做缓存,效果还不错~不过现在碰到了标准化Store里取关联数据的场景,写了嵌套的selector实现对吧?先把你当前的代码整理出来(补全了截断的部分,应该和你实际写的差不多):
const getItemId = props => parseInt(props.match.params.itemId, 10) const containerSelector = createSelector( getItemId, itemId => createSelector( [getUser, getItem(itemId)], (user, item) => createSelector( [ getFoo(item), getBar(user, item) ], (foo, bar) => ({ user, item, foo, bar }) ) ) )
这种嵌套创建selector的方式确实能正常跑,但其实有点绕,而且每次getItemId返回新的itemId时,中间那层createSelector([getUser, getItem(itemId)], ...)都会重新创建一个新的selector实例。虽然Reselect的缓存机制还是能生效,但写法上可以更清晰高效,也能避免不必要的实例创建。
优化方案:拆分单一职责的参数化选择器
这是Reselect的最佳实践,把每个选择器拆成只做一件事的模块,既清晰又能最大化利用缓存:
// 1. 基础选择器:从Store里取标准化的数据集 const selectUsers = state => state.users const selectItems = state => state.items const selectFoos = state => state.foos const selectBars = state => state.bars // 2. 参数化选择器:根据ID或关联字段取单个数据 const selectCurrentUser = createSelector( [selectUsers, state => state.currentUserId], (users, userId) => users[userId] ) const selectItemById = createSelector( [selectItems, (state, itemId) => itemId], (items, itemId) => items[itemId] ) const selectFooByItem = createSelector( [selectFoos, (state, item) => item?.fooId], (foos, fooId) => foos[fooId] ) const selectBarByUserAndItem = createSelector( [selectBars, (state, user) => user?.id, (state, item) => item?.id], (bars, userId, itemId) => bars[`${userId}_${itemId}`] ) // 3. 最终容器选择器:组合所有需要的关联数据 const containerSelector = createSelector( // 从props里拿到itemId (state, props) => parseInt(props.match.params.itemId, 10), // 获取当前用户 state => selectCurrentUser(state), // 根据itemId拿到对应的item (state, itemId) => selectItemById(state, itemId), // 基于item拿到关联的foo (state, itemId, user, item) => selectFooByItem(state, item), // 基于user和item拿到关联的bar (state, itemId, user, item) => selectBarByUserAndItem(state, user, item), // 组装最终需要的组件数据 (itemId, user, item, foo, bar) => ({ user, item, foo, bar }) )
为什么这种写法更好?
- 单一职责:每个选择器只负责一件事,比如
selectItemById就只根据ID从items集合里取数据,后期维护起来特别方便; - 缓存高效:每个参数化选择器只会在依赖的输入数据变化时重新计算,比如
selectFooByItem只会在selectFoos或者传入的item.fooId变化时才会重新执行; - 可读性强:整个数据依赖链一目了然,不用嵌套几层去猜逻辑。
如果实在喜欢嵌套的写法,也可以把中间层的selector提前定义好,避免动态创建实例,但还是推荐上面的拆分方案哦~
内容的提问来源于stack exchange,提问作者Pandaiolo
相关产品推荐
相关产品推荐

