Reduxjs/Reselect 项目中的结构化选择器实践指南
什么是结构化选择器
在 Redux 应用中,选择器(Selector)是从 Redux 状态树中提取和派生数据的函数。Reselect 库提供了创建记忆化选择器的功能,而 createStructuredSelector
是 Reselect 提供的一个实用工具,它可以帮助我们更优雅地组织多个选择器。
结构化选择器的基本用法
从示例代码中我们可以看到两种等效的选择器创建方式:
// 使用 createStructuredSelector
export const structuredSelector = createStructuredSelector(
{
todos: (state: RootState) => state.todos,
alerts: (state: RootState) => state.alerts,
todoById: (state: RootState, id: number) => state.todos[id]
},
createSelector
)
// 等效的直接使用 createSelector
export const selector = createSelector(
[
(state: RootState) => state.todos,
(state: RootState) => state.alerts,
(state: RootState, id: number) => state.todos[id]
],
(todos, alerts, todoById) => {
return {
todos,
alerts,
todoById
}
}
)
为什么使用结构化选择器
-
代码可读性:结构化选择器通过对象字面量的方式定义,使得选择器与其返回的属性名直接对应,提高了代码的可读性。
-
维护便利性:当需要添加或删除选择器时,只需在对象中增删相应的属性,而不需要修改多个地方的代码。
-
类型安全:在 TypeScript 中,结构化选择器能够更好地保持类型信息,减少类型错误的可能性。
实际应用场景
假设我们有一个 Redux 应用,状态树包含待办事项(todos)和通知(alerts)两部分:
interface RootState {
todos: {
id: number
completed: boolean
title: string
description: string
}[]
alerts: { id: number; read: boolean }[]
}
场景1:组合多个状态片段
当组件需要同时访问多个不相关的状态片段时,结构化选择器特别有用。例如,一个页面可能需要显示待办事项列表和未读通知数量:
const pageDataSelector = createStructuredSelector({
todos: (state: RootState) => state.todos,
unreadAlerts: (state: RootState) =>
state.alerts.filter(alert => !alert.read).length
})
场景2:带参数的选择器
如示例所示,结构化选择器也可以处理带参数的选择器。todoById
选择器接收一个额外的 id
参数:
todoById: (state: RootState, id: number) => state.todos[id]
这在需要根据组件props动态选择数据时非常有用。
性能优化考虑
结构化选择器底层仍然使用 Reselect 的记忆化机制,这意味着:
- 只有当输入选择器的结果发生变化时,才会重新计算
- 如果所有输入选择器的结果与上次相同,将直接返回缓存的结果
- 每个选择器独立记忆化,互不影响
最佳实践建议
-
命名一致性:保持选择器属性名与状态树中的属性名一致,提高代码可维护性
-
复杂派生逻辑:对于需要复杂计算的派生数据,建议先创建独立的选择器,再组合到结构化选择器中
-
类型注解:在 TypeScript 中,为 RootState 和选择器返回值添加明确的类型注解
-
适度使用:不是所有场景都需要结构化选择器,简单的单一数据选择可以直接使用
createSelector
总结
Reselect 的 createStructuredSelector
提供了一种清晰、可维护的方式来组合多个选择器。它特别适合需要从 Redux 状态中提取多个不相关数据片段的场景,同时保持了 Reselect 的记忆化优势。通过合理使用结构化选择器,可以使 Redux 应用的代码更加模块化和易于维护。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考