Redux概览
Redux 是什么?
Redux
是一个使用叫做 action
的事件来管理和更新应用状态的模式和工具库它以集中式 Store(centralized store)的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。
为什么使用 Redux
Redux
可以帮助我们管理全局状态。
什么时候用 Redux
- 应用中有很多 state 在多个组件中需要使用 (频繁的在组件见传递
state
) - 应用 state 会随着时间的推移而频繁更新 (例如: drag/resize/interval)
- 更新 state 的逻辑很复杂
- 中型和大型代码量的应用,很多人协同开发
推荐使用 Redux 的方式: react-redux
+ redux-toolkit(@reduxjs/toolkit)
+ redux devtools
Redux 组成
- state: 驱动应用的真实数据源头
- view: 基于当前状态的视图的声明性描述
- actions: 根据用户触发的事件,并触发状态更新
单向数据流 (one-way data flow)
state
描述应用程序在某个时间点的状态- 基于
state
渲染view
- 当用户触发事件(例如点击按钮), state 会根据发生的事件进行更新,生成新的
State
(注意不是修改State
) - 基于新的
state
重新渲染view
通过定义和分离 state 管理中涉及的概念并强制执行维护 view 和 state 之间独立性的规则,代码变得更结构化和易于维护。
这就是 Redux 背后的基本思想:应用中使用集中式的全局状态来管理,并明确更新状态的模式,以便让代码具有可预测性。
不可变性 Immutability
JavaScript 的对象(object)和数组(array)默认都是 mutable 的。如果我创建一个对象,我可以更改其字段的内容。如果我创建一个数组,我也可以更改内容:
const obj = { a: 1, b: 2 };
// 对外仍然还是那个对象,但它的内容已经变了\
// 引用相同,值改变
obj.b = 3;
const arr = ["a", "b"];
// 同样的,数组的内容改变了
arr.push("c");
arr[1] = "d";
这就是 改变 对象或数组的例子。内存中还是原来对象或数组的引用,但里面的内容变化了。(改变了值但是引用相同)
如果想要不可变的方式来更新,代码必需先复制原来的 object/array,然后更新它的复制体。
Redux 期望所有状态更新都是使用不可变的方式。
术语
Action
action是一个具有 type 字段的普通 JavaScript 对象。你可以将 action 视为描述应用程序中发生了什么的事件。
type
字段是一个字符串,给这个 action
一个描述性的名字,比如 "todos/todoAdded"
。我们通常把那个类型的字符串写成“域/事件名称”,其中第一部分是这个 action
所属的特征或类别,第二部分是发生的具体事情。
action
对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为 payload
的字段中。
一个典型的 action
对象可能如下所示:
const addTodoAction = {
type: "todos/todoAdded",
payload: "Buy milk",
};
Reducer
reducer
是一个函数,接收当前的 state
和一个 action
对象,必要时决定如何更新状态,并返回新状态。函数签名是:(state, action) => newState
。 你可以将 reducer
视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。
"Reducer" 函数的名字来源是因为它和 Array.reduce() 函数使用的回调函数很类似。
Reducer
必需符合以下规则:
- 仅使用
state
和action
参数计算新的状态值 - 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates)。
- 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码。
reducer 函数内部的逻辑通常遵循以下步骤:
- 检查 reducer 是否关心这个 action
- 如果是,则复制 state,使用新值更新 state 副本,然后返回新 state
- 否则,返回原来的 state 不变
下面是 reducer
的小例子,展示了每个 reducer
应该遵循的步骤:
const initialState = { value: 0 };
function counterReducer(state = initialState, action) {
// 检查 reducer 是否关心这个 action
if (action.type === "counter/increment") {
// 如果是,复制 `state`
return {
...state,
// 使用新值更新 state 副本
value: state.value + 1,
};
}
// 返回原来的 state 不变
return state;
}
Action Creator
action creator
是一个创建并返回一个 action
对象的函数。它的作用是让你不必每次都手动编写 action
对象:
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
store
当前 Redux
应用的 state
存在于一个名为 store
的对象中。
store 是通过传入一个 reducer 来创建的,并且有一个名为 getState 的方法,它返回当前状态值。
Dispatch
Redux store 有一个方法叫 dispatch
。更新 state
的唯一方法是调用 store.dispatch()
并传入一个 action
对象。 store
将执行所有 reducer
函数并计算出更新后的 state,调用 getState()
可以获取新 state。
store.dispatch({ type: "counter/increment" });
dispatch 一个 action 可以形象的理解为 "触发一个事件"。发生了一些事情,我们希望 store 知道这件事。 Reducer 就像事件监听器一样,当它们收到关注的 action 后,它就会更新 state 作为响应。
我们通常调用 action creator 来调用 action:
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
console.log(store.getState())
// {value: 2}
Selector
Selector
函数可以从 store 状态树中提取指定的片段。随着应用变得越来越大,会遇到应用程序的不同部分需要读取相同的数据,selector
可以避免重复这样的读取逻辑:
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2