Redux 是一个很棒的用于管理应用程序“状态”的工具。单向数据流以及对不可变数据的关注使得推断状态的变化变得很简单。每次状态变化都由一个 action 触发,这会导致 reducer 函数返回一个变更后的新状态。由于客户要在我们的平台上管理或发布广告资源,在 AppNexus 使用 Redux 创建的很多用户界面都需要处理大量数据以及非常复杂的交互。在开发这些界面的过程中,我们发现了一些有用的规则和技巧以维持 Redux 易于管理。以下的几点讨论应该可以帮助到任何在大型,数据密集型应用中使用 Redux 的开发者:
第一点: 在存储和访问状态时使用索引和选择器 第二点: 把数据对象,对数据对象的修改以及其它 UI 状态区分开 第三点: 在单页应用的不同页面间共享数据,以及何时不该这么做 第四点: 在状态中的不同节点复用通用的 reducer 函数 第五点: 连接 React 组件与 Redux 状态的最佳实践 1. 使用索引保存数据,使用选择器读取数据 选择正确的数据结构可以对程序的结构和性能产生很大影响。存储来自 API 的可序列化数据可以极大的受益于索引的使用。索引是指一个 JavaScript 对象,其键是我们要存储的数据对象的 id,其值则是这些数据对象自身。这种模式和使用 hashmap 存储数据非常类似,在查询效率方面也有相同的优势。这一点对于精通 Redux 的人来说不足为奇。实际上,Redux 的作者 Dan Abramov 在它的 Redux 教程中就推荐了这种数据结构。
设想你有一组从 REST API 获取的数据对象,例如来自 /users 服务的数据。假设我们决定直接将这个普通数组存储在状态中,就像在响应中那样。当我们需要获取一个特定用户对象时会怎样呢?我们需要遍历状态中的所有用户。如果用户很多,这可能会是一个代价高昂的操作。如果我们想跟踪用户的一小部分,例如选中和未选中的用户呢?我们要么需要把数据保存在两个数组中,要么就要跟踪这些选中和未选中用户在主数组中的索引(译者注:此处指的是普通意义上的数组索引)。
然而,我们决定重构代码改用索引的方式存储数据。我们可以在 reducer 中以如下的方式存储数据:
{ "usersById": { 123: { id: 123, name: "Jane Doe", email: "jdoe@example.
嗯,就是学习一下,读读源码。顺便把源码里的文档翻译了一下并添加了一些简单的注释。
本文对应的文件是src/createStore.js
import isPlainObject from 'lodash/isPlainObject' import $$observable from 'symbol-observable' /** * ActionTypes里定义的是Redux保留的私有action。 * 对于任何未知的action,你必须返回store的当前状态。 * 如果传入的当前状态是undefined,你必须返回store的初始状态。 * 不要在应用代码中直接引用这些action。 */ export var ActionTypes = { INIT: '@@redux/INIT' } /** * createStore方法用于创建一个保存程序状态的store。 * 改变store中数据的唯一方法是调用store的`dispatch()`方法。 * * 你的应用中应该只有一个store。为了将程序状态中不同部分的变更逻辑 * 组合在一起,你可以通过`combineReducers`方法将多个reducer组合成一个reducer。 * * @param {Function} reducer 一个返回应用下一状态的函数,入参是程序的当前状态以及 * 要发送的action。 * * @param {any} [preloadedState] store的初始状态。你可以选择性的为store指定一个 * 初始状态。 * 如果你使用了`combineReducers`方法来生成最终的reducer。那么这个初始状态对象的 * 结构必须与调用`combineReducers`方法时传入的参数的结构保持对应关系。 * * @param {Function} enhancer store增强器。你可以选择性的传入一个增强函数来扩展 * store的功能,例如中间件,时间旅行,持久化等。Redux自带的唯一一个增强器是 * `applyMiddleware()`方法。 * * @returns {Store} 返回一个可以读取状态,发送action以及订阅变更通知的Redux store。 */ export default function createStore(reducer, preloadedState, enhancer) { // 如果只传入reducer和enhancer,则store的初始状态为undefined if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } // enhancer必须是一个函数 if (typeof enhancer !