
在 reselect 中,通过闭包(如 `customerid => createselector(...)`)创建带参数的选择器会导致每次渲染都生成新实例,使 memoization 完全失效,引发重复计算和内存浪费;而官方推荐的多参数 `createselector` 方式才能真正复用缓存。
Reselect 的核心价值在于共享、可复用、带缓存的选择器实例。其 memoization 机制依赖于两个关键前提:
- 选择器函数是稳定引用(即不随渲染重新创建);
- 输入参数能被正确捕获并参与缓存键计算。
❌ 错误方式:闭包工厂模式(性能陷阱)
// 危险!每次调用 selectOrdersByCustomer(customerId) 都创建全新 selector 实例
const selectOrdersByCustomer = customerId =>
createSelector(
state => state.orders,
orders => {
console.count('⚠️ output selector executed'); // 每次渲染都触发!
return orders.filter(order => order.customerId === customerId);
}
);
// 在组件中:
const orders = useSelector(selectOrdersByCustomer(customerId));该写法看似简洁,实则违背 Reselect 设计原则:
- selectOrdersByCustomer(customerId) 每次调用返回一个全新的 createSelector 实例;
- 每个实例拥有独立的缓存(recomputations()、memoizedResultSelector),互不共享;
- 即使 state.orders 和 customerId 均未变化,因 selector 实例不同,缓存永远无法命中 → 输出选择器反复执行,且返回新数组(=== false)。
✅ 实验验证(见原答案日志):三次调用 selectOrdersByCustomer2(1)(state) 导致输出选择器执行 3 次,且结果引用不等(false false)。
✅ 正确方式:多参数选择器(推荐标准写法)
// ✅ 稳定声明:单个 selector 实例,支持参数化输入
const selectOrdersByCustomer = createSelector(
state => state.orders,
(state, customerId) => customerId, // 第二个输入选择器,接收 props 参数
(orders, customerId) => {
console.count('✅ memoized output selector'); // 仅当 orders 或 customerId 变化时执行
return orders.filter(order => order.customerId === customerId);
}
);
// 在组件中(useSelector 自动透传第二个参数):
const orders = useSelector(state => selectOrdersByCustomer(state, customerId));此方式优势显著:
- selectOrdersByCustomer 是单一、稳定的函数引用,可安全定义在模块顶层;
- Reselect 内部将 (state, customerId) 作为缓存键(equalityCheck 默认为 ===),确保相同参数组合命中缓存;
- 多次调用 selectOrdersByCustomer(state, 1) 仅首次执行过滤逻辑,后续直接返回缓存结果(=== true)。
⚠️ 注意事项与最佳实践
- 避免在组件内部或 useSelector 回调中动态创建 selector(包括闭包、useMemo(() => createSelector(...)) 等);
- 若需动态生成 selector(极少数场景),应配合 useMemo 缓存 factory 函数本身,并确保 customerId 是 useMemo 的依赖项——但通常没必要,优先用多参数模式;
- 对于复杂参数(如对象),注意 Reselect 默认浅比较(===),必要时自定义 memoize 或 equalityCheck;
- 使用 createSelectorCreator + lodash.memoize 可扩展缓存策略,但默认方案已满足绝大多数场景。
总结
闭包式 selector 不是“稍慢一点”,而是彻底丢失 memoization 能力——它把 Reselect 降级为普通纯函数,还额外增加了 selector 创建开销。务必坚持 Reselect 官方范式:参数通过输入选择器传入,selector 实例全局唯一、稳定复用。这是保障 React-Redux 应用高性能的关键细节之一。











