Content #
Redux 引入的概念其实并不多,主要就是三个:State、Action 和 Reducer。
- 其中 State 即 Store,一般就是一个纯 JavaScript Object。
- Action 也是一个 Object,用于描述发生的动作。
- 而 Reducer 则是一个函数,接收 Action 和 State 并作为参数,通过计算得到新的 Store。
它们三者之间的关系可以用下图来表示:
在 Redux 中,所有对于 Store 的修改都必须通过这样一个公式去完成,即通过 Reducer 完成,而不是直接修改 Store。这样的话,一方面可以保证数据的不可变性(Immutable),同时也能带来两个非常大的好处。
- 可预测性(Predictable):即给定一个初始状态和一系列的 Action,一定能得到一致的结果,同时这也让代码更容易测试。
- 易于调试:可以跟踪 Store 中数据的变化,甚至暂停和回放。因为每次 Action 产生的变化都会产生新的对象,而我们可以缓存这些对象用于调试。 Redux 的基于浏览器插件的开发工具就是基于这个机制,非常有利于调试。
比如说要实现“加一”和“减一”这两个功能,对于 Redux 来说,我们需要如下代码:
import { createStore } from 'redux'
// 定义 Store 的初始值
const initialState = { value: 0 }
// Reducer,处理 Action 返回新的 State
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
// 利用 Redux API 创建一个 Store,参数就是 Reducer
const store = createStore(counterReducer)
// Store 提供了 subscribe 用于监听数据变化
store.subscribe(() => console.log(store.getState()))
// 计数器加 1,用 Store 的 dispatch 方法分发一个 Action,由 Reducer 处理
const incrementAction = { type: 'counter/incremented' };
store.dispatch(incrementAction);
// 监听函数输出:{value: 1}
// 计数器减 1
const decrementAction = { type: 'counter/decremented' };
store.dispatch(decrementAction)
// 监听函数输出:{value: 0}
通过这段代码,我们就用三个步骤完成了一个完整的 Redux 的逻辑:
- 先创建 Store;
- 再利用 Action 和 Reducer 修改 Store;
- 最后利用 subscribe 监听 Store 的变化。
需要注意的是,在 Reducer 中,我们每次都必须返回一个新的对象,确保不可变数据(Immutable)的原则。一般来说,我们可以用延展操作符(Spread Operator)来简单地实现不可变数据的操作,例如:
return {
...state, // 复制原有的数据结构
value: state.value + 1, // 变化 value 值使其 + 1
}
这在大多数场景下已经足够使用。当然对于复杂的数据结构,也有一些第三方的库可以帮助操作不可变数据,比如 Immutable、Immer 等等。
通过这个例子,我们看到了纯 Redux 使用的场景,从而更加清楚地看到了 Store、Action 和 Reducer 这三个基本概念,也就能理解 State + Action => New State 这样一个简单却核心的机制。