跳至主内容

设计决策

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

Redux 常见问题解答:设计决策

为什么 Redux 不将 state 和 action 传递给订阅者?

订阅者应当响应 state 值本身而非 action。state 更新是同步处理的,但订阅者通知可能被批量处理或防抖,这意味着订阅者不会在每次 action 时都被通知。这是常见的性能优化手段,可避免重复渲染。

通过 enhancer 覆盖 store.dispatch 可以批量处理或防抖订阅通知。此外,这些库能批量处理 action 以优化性能并避免重复渲染:

Redux 确保最终会以最新 state 调用所有订阅者,但不保证每次 action 都调用每个订阅者。订阅者中可通过 store.getState() 获取 store state。若将 action 暴露给订阅者会破坏批量处理机制。

在订阅者中使用 action 的潜在场景(非官方支持)是确保组件仅在特定 action 后重渲染。正确的重渲染控制应通过:

  1. shouldComponentUpdate 生命周期方法

  2. 虚拟 DOM 比对(vDOMEq)

  3. React.PureComponent

  4. React-Redux:使用 mapStateToProps 让组件仅订阅所需 store 部分

更多信息

文章

讨论

为什么 Redux 不支持使用类作为 action 和 reducer?

使用函数(action creator)返回 action 对象的设计,对习惯面向对象编程的开发者可能反直觉——他们更倾向使用类和实例。Redux 不支持类实例形式的 action 和 reducer,因为序列化/反序列化时:JSON.parse(string) 会返回普通 JS 对象而非类实例。

Store 常见问题所述,若不介意持久化和时间旅行调试等功能受限,可将非序列化项放入 Redux store。

序列化使浏览器能以更少内存存储所有已分发 action 和先前 store 状态。"回退"和"热重载"是 Redux 开发者体验和 DevTools 的核心功能。这也支持在服务端渲染时,将反序列化 action 存储于服务端并在浏览器重新序列化。

更多信息

文章

讨论

为什么中间件签名使用柯里化(Currying)?

Redux 中间件采用三层嵌套函数结构(形如 const middleware = storeAPI => next => action => {}),而非单层函数结构(如 const middleware = (storeAPI, next, action) => {}),这有几点原因:

首先,函数"柯里化"是函数式编程的标准技术,而 Redux 在设计上明确采用了函数式编程原则。其次,柯里化函数会创建闭包,允许在中间件生命周期内声明持久变量(可视为类实例变量的函数式等效)。最后,这仅仅是 Redux 最初设计时选择的实现方式。

有人认为声明中间件的柯里化函数签名并非必需,因为在执行 applyMiddleware 时 store 和 next 均已可用。但此问题已被确定为不值得引入破坏性变更,因为当前 Redux 生态中有数百个中间件依赖现有签名。

更多信息

讨论

为什么 applyMiddleware 使用闭包处理 dispatch

applyMiddleware 通过闭包封装 store 原有的 dispatch 方法,创建中间件初始调用链。中间件被调用时会接收包含 getState 和 dispatch 方法的对象,这使得初始化时依赖 dispatch 的中间件能够正确运行。

更多信息

讨论

  • 为什么 applyMiddleware 使用闭包处理 dispatch?

为什么 combineReducers 不将整个 state 作为第三个参数传递给每个 reducer?

combineReducers 的设计理念是鼓励按功能域拆分 reducer 逻辑。如超越 combineReducers所述,combineReducers 被刻意限定于单一常见场景:通过委托各状态切片给专属 reducer 来更新普通 JavaScript 对象状态树。

若添加第三个参数(整个状态树/回调函数/其他状态片段等)会引发歧义。如果 combineReducers 不能满足你的需求,当需要深度嵌套的 reducer 或需访问全局状态时,可考虑使用替代方案如 combineSectionReducersreduceReducers

如果现有工具都无法满足您的需求,您完全可以自行编写一个完全符合需求的函数。

扩展阅读

文章

讨论

为何不允许在 mapDispatchToProps 中使用 getState()mapStateToProps() 的返回值?

存在这样的需求:希望在 mapDispatch 内部使用整个 statemapState 的返回值,这样在 mapDispatch 中声明的函数就能访问存储中的最新返回值。

mapDispatch 不支持此方法,因为这将导致每次存储更新时都要调用 mapDispatch。会造成每次状态更新都重新创建函数,从而带来严重的性能开销。

处理此场景的首选方案(需要基于当前状态和 mapDispatchToProps 函数调整属性)是通过 connect 函数的第三个参数 mergeProps 实现。如果指定该参数,它会接收 mapStateToProps()mapDispatchToProps() 和容器组件属性的结果。mergeProps 返回的普通对象将作为属性传递给被包装组件。

扩展阅读

讨论