如何实现Selector结果映射及多WebSocket数据源数据聚合?
问题解答
1. 当前selectLiveBalances写法的可行性
功能上这个写法能生成你需要的数组,但存在性能隐患:
- 每次调用
balances.map(...)都会返回全新的数组引用,哪怕内部每个selectLiveBalanceByCode的结果都没变化,这个selector也会触发下游(比如求和selector)重新计算。 - 如果
selectLiveBalances是基于createCachedSelector定义的,但当前写法没正确配置缓存依赖/key,缓存机制大概率没生效,等于浪费了createCachedSelector的优化能力。
2. 优化后的聚合Selector方案
第一步:确保单个余额Selector的缓存有效性
先保证selectLiveBalanceByCode是正确memoized的,能稳定合并两个adapter的数据:
import { createSelector } from '@reduxjs/toolkit' // 合并两个adapter的单币种余额数据 export const selectLiveBalanceByCode = createSelector( [ (state, code) => state.balanceAdapter1.entities[code], (state, code) => state.balanceAdapter2.entities[code] ], (balanceFromAdapter1, balanceFromAdapter2) => { // 根据需求合并计算,这里以金额求和为例 return { code, total: (balanceFromAdapter1?.amount || 0) + (balanceFromAdapter2?.amount || 0), // 可添加其他需要的字段,比如两个adapter各自的明细 adapter1Amount: balanceFromAdapter1?.amount || 0, adapter2Amount: balanceFromAdapter2?.amount || 0 } as LiveBalanceValue } )
第二步:修正selectLiveBalances的缓存逻辑
用createCachedSelector结合自定义缓存key,确保只有当币种列表或对应余额变化时才重新生成数组:
import { createCachedSelector } from 'reselect' export const selectLiveBalances = createCachedSelector( [selectBalanceCurrencies, (state) => state], (currencyCodes, state) => { return currencyCodes.map(code => selectLiveBalanceByCode(state, code)) }, { // 用币种列表的拼接字符串作为缓存key,比JSON.stringify更高效 keySelector: (state) => selectBalanceCurrencies(state).join(',') } )
第三步:实现总计与部分总计Selector
基于优化后的selectLiveBalances,直接做聚合计算即可,reselect会自动缓存求和结果:
// 所有余额的总计 export const selectTotalLiveBalance = createSelector( [selectLiveBalances], (balances) => { return balances.reduce((sum, item) => sum + item.total, 0) } ) // 自定义筛选的部分总计(支持传入指定币种列表) export const selectPartialLiveBalance = createSelector( [selectLiveBalances, (_, targetCodes) => targetCodes], (balances, targetCodes) => { return balances .filter(item => targetCodes.includes(item.code)) .reduce((sum, item) => sum + item.total, 0) } )
关键注意点
- 缓存key优化:如果币种代码是字符串/数字,用
join(',')比JSON.stringify性能更好;如果是复杂类型,再考虑用字符串序列化。 - 保持纯函数:所有Selector里不能有异步操作、修改外部状态等副作用,确保输入相同则输出一定相同。
- 依赖精准性:每个Selector的依赖要精准,避免不必要的重新计算。
内容的提问来源于stack exchange,提问作者Myles Wardell
相关产品推荐
相关产品推荐

