Redux 项目中的选择器(Selectors)使用指南:数据派生与性能优化

Redux 项目中的选择器(Selectors)使用指南:数据派生与性能优化

redux redux 项目地址: https://gitcode.com/gh_mirrors/red/redux

什么是选择器?

在 Redux 应用中,选择器(Selector)是一种用于从 Redux 状态树中提取和派生数据的函数。选择器的主要作用是封装状态访问逻辑,提高代码的可维护性,并通过记忆化(memoization)优化性能。

为什么需要选择器?

最小化状态原则

Redux 推崇最小化状态的设计理念,即只存储最原始的数据,其他派生数据都通过计算得到。例如:

  • 待办事项应用存储原始任务列表,过滤后的列表通过计算获得
  • 购物车存储商品条目,总价通过计算获得

这种设计带来三大优势:

  1. 状态树更简洁易读
  2. 减少维护派生数据一致性的逻辑
  3. 原始数据始终保持不变,便于追溯

React 中的相同原则

这一原则同样适用于 React 本地状态管理。很多开发者习惯在 useEffect 中计算派生状态并设置到 state 中,但实际上更好的做法是在渲染过程中直接计算:

function TodoList() {
  const [todos] = useState([])
  // 直接在渲染时计算派生值
  const allCompleted = todos.every(todo => todo.completed)
  // 使用该值进行渲染
}

选择器基础

基本概念

选择器函数接收整个 Redux 状态(或部分状态)作为参数,返回基于该状态的派生数据。选择器没有特殊格式要求,以下都是有效的选择器:

// 直接取值
const selectProducts = state => state.products

// 数组映射
const selectProductIds = state => 
  state.products.map(product => product.id)

// 深层取值
const selectUserProfile = state =>
  state.user.profile

命名规范

建议选择器名称以 select 开头,后接描述性内容,例如:

  • selectUserById
  • selectCompletedTodos
  • selectVisibleProducts

使用场景

选择器可用于:

  1. useSelector 钩子
  2. connectmapState
  3. Redux 中间件
  4. Thunk 和 Saga
// 在 thunk 中使用选择器
function addToCart(productId) {
  return (dispatch, getState) => {
    const state = getState()
    const canAdd = selectCanAddToCart(state)
    if (canAdd) {
      dispatch(cartAdded(productId))
    }
  }
}

选择器的封装价值

状态形状封装

直接访问深层状态会导致代码脆弱:

// 脆弱写法 - 直接访问深层状态
const data = useSelector(state => state.some.deeply.nested.field)

使用选择器封装后,状态结构调整只需修改选择器:

// 封装后的选择器
const selectSomeData = state => state.some.deeply.nested.field

// 组件中使用
const data = useSelector(selectSomeData)

最佳实践:只有 reducer 和选择器应该知道确切的状态结构。

记忆化选择器

性能问题

原始选择器存在两个性能隐患:

  1. 每次 action 后都会重新计算,即使相关状态未变化
  2. 总是返回新引用,导致不必要的组件重渲染
// ❌ 性能问题:总是返回新数组
const completedTodos = useSelector(state =>
  state.todos.map(todo => todo.completed))

Reselect 解决方案

Reselect 库提供 createSelector 创建记忆化选择器:

import { createSelector } from 'reselect'

const selectTodos = state => state.todos

// 记忆化选择器
const selectCompletedTodos = createSelector(
  [selectTodos], // 输入选择器
  todos => todos.filter(todo => todo.completed) // 转换函数
)

工作原理:

  1. 缓存输入选择器的结果
  2. 只有输入变化时才重新计算
  3. 输入相同时返回缓存结果

高级用法

多参数选择器
const selectItems = state => state.items
const selectCategory = (state, category) => category

const selectItemsByCategory = createSelector(
  [selectItems, selectCategory],
  (items, category) => items.filter(item => item.category === category)
)

// 使用
const electronics = selectItemsByCategory(state, 'electronics')
选择器工厂

当需要创建多个独立缓存的选择器实例时:

const makeSelectItemsByCategory = () => 
  createSelector(
    [selectItems, selectCategory],
    (items, category) => items.filter(item => item.category === category)
  )

// 不同组件使用不同实例
const selectElectronics = makeSelectItemsByCategory()
const selectClothing = makeSelectItemsByCategory()

最佳实践

  1. 保持选择器纯净:不产生副作用
  2. 输入选择器应简单:只负责取值
  3. 复杂逻辑放在输出选择器:转换和计算
  4. 避免 state => state:这会破坏记忆化
  5. 合理组织选择器:与相关 reducer 放在一起
// ✅ 良好实践
const selectUser = state => state.user
const selectOrders = state => state.orders

const selectUserOrders = createSelector(
  [selectUser, selectOrders],
  (user, orders) => orders.filter(o => o.userId === user.id)
)

通过遵循这些原则,您可以构建出高效、可维护的 Redux 状态选择系统,显著提升应用性能。

redux redux 项目地址: https://gitcode.com/gh_mirrors/red/redux

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强懿方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值