这是我参与「第四届青训营 」笔记创作活动的的第6天。在青训营的低代码项目开发中,编辑组件间状态的混乱,随着代码的不断堆砌,对开发进展的影响变得越来越严重。之前因为使用的主要是 Vue 框架,对于状态管理的意识较为淡薄,也借此机会学习了解一下 React 中状态管理工具 Redux 的基础知识与代码实践。
什么是 Redux
Redux 是一个使用叫做“action”的事件来管理和更新应用状态的模式和工具库 它以集中式Store(centralized store)的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。—— Redux 基本思想
Redux 提供的工具可以帮助开发者在前端项目中管理“全局”状态。
使用这样的管理工具(以声明式的方式)可以让开发者更容易理解程序状态在何时何地、为什么更新以及如何更新,有利于提升开发者对程序状态的控制能力。
什么时候使用 Redux
Redux 不是必要的(可以不用),但有的时候又是必要的(不得不用,用了就爽)。作为一款状态管理工具,其工具属性表明它可以被一些通用工具(React 组件中的状态管理)所替代,但确实解决了开发中一些情况下的痛点。而什么时候在项目中使用它,就是开发者应当去学习、思考的问题了。(在后续的部分也阐明了部分对此问题的思考)
适用情况:
- 在应用的大量地方,都存在大量的状态
- 应用状态会随着时间的推移而频繁更新
- 更新该状态的逻辑可能很复杂
- 中型和大型代码量的应用,很多人协同开发
Redux 术语与概念
建议在了解 Redux 整体后反复阅读理解
state 管理
state -> view -> action -> state 是组件中常见的数据流向。
- state:驱动应用的真实数据源头
- view:基于当前状态的 UI 声明性描述
- actions:根据用户输入在应用程序中发生的事件,并触发状态更新
多个组件需要共享和使用相同state时,“提升 state”的方法并不总是有效的。Redux 共享 state 可以解决此问题。
不可变性 Immutability
JS 中的对象是可变的(mutable),修改对象中的内容,不会导致 obj 的引用变化,而 obj 对象确实发生了改变。
const obj = { a: 1, b: 2 }\
// obj 仍然还是那个对象,但它的内容已经变了,数组同理
obj.b = 3
使用不可变的方式更新对象,必须复制原来的对象,更新复制体。(在 React 中使用可变方式的更新(上面代码块的方式)大多不会触发页面的重新渲染 —— React 认为当前的 state 还是原来的 state。)
const obj = {
a: {
// 为了安全的更新 obj.a.c,需要先复制一份
c: 3
},
b: 2
}
const obj2 = {
// obj 的备份
...obj,
// 覆盖 a
a: {
// obj.a 的备份
...obj.a,
// 覆盖 c
c: 42
}
}
Redux 组成
-
State 状态
- DomainData:服务端数据
- UI State:决定当前 UI 的状态
- App State:APP级别的状态
-
Action 事件
数据从应用传递到 store 的载体,是 store 数据唯一的来源
- 本质上是一个 JS obj
- 内部有 type 属性表示执行的动作,多数情况下是字符串常量
- 多数为纯函数,描述一个执行的过程(声明式),不直接更新 State
-
Reducer
本质是一个函数,用于响应发送过来的 action 数据,把 state 发送给 Store
- 仅使用
state和action参数计算新的状态值 - 禁止直接修改
state。必须通过复制现有的state并对复制的值进行更改的方式来做 不可变更新(immutable updates) 。 - 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码
- 仅使用
-
Store
把 action 和 reducer 联系到一起的对象
- 维持应用的 state,createStore() 构建 store
- 关联 action 和 reducer
- 提供 getState() 获取 State
- 提供 dispatch() 发送 action
- 提供 subscribe() 注册监听,返回值注销监听。响应 reducer 返回值
Redux 实践
- Redux store 是使用 Redux Toolkit 中的
configureStore函数创建 - Redux 切片(Slice)是应用中单个功能的 Redux reducer 逻辑和 action 的集合,通常一起定义在一个文件中
来自 Redux 官网的 counter 样例: redux-essentials-counter-example - CodeSandbox
-
createSlice内部使用了一个名为 Immer 的库。 Immer 使用一种称为 “Proxy” 的特殊 JS 工具来包装您提供的数据。使开发者在其中可以使用 mutate 的方式更新值。上述改变的内容为 state 中的内容(对象的参数),state (直接索引值)依然是不可直接改变的,否则会导致一系列问题。
-
thunk 是一种特定类型的 Redux 函数,可以包含 异步逻辑,Redux Toolkit 的
configureStore函数能够自动配置 thunk,所以可以直接使用异步函数编写 reducer。
hooks
解决了在组件中无法访问到 store 的问题。
-
useSelector提取数据 -
useDispatchdispatch action