Redux总结

版权声明:可以装载,装载请在第一行注明出处。本人文章如有侵权,请联系本人删除(或在评论区评论让我进行删除) https://blog.csdn.net/weixin_43532346/article/details/85170244

如何在 reducer 之间共享 state? combineReducers 是必须的吗?

Redux store 推荐的结构是将 state 对象按键值切分成 “层”(slice) 或者 “域”(domain),并提供独立的 reducer 方法管理各自的数据层。就像 Flux 模式中的多个独立 store 一样, Redux 为此还提供了 combineReducers 工具来简化该模型。应当注意的是, combineReducers 不是 必须的,它仅仅是通过简单的 JavaScript 对象作为数据,让 state 层能与 reducer 一一关联的函数而已。

reducer 仅仅是函数,可以随心所欲的进行划分和组合,而且也推荐将其分解成更小、可复用的函数 (“reducer 合成”)。按照这种做法,如果子 reducer 需要一些参数时,可以从父 reducer 传入。你只需要确保他们遵循 reducer 的基本准则: (state, action) => newState,并且以不可变的方式更新 state,而不是直接修改 state。

处理 action 必须用 switch 语句吗?

不是。在 reducer 里面你可以使用任何方法响应 action。 switch 语句是最常用的方式,当然你也可以用 if、功能查找表、创建抽象函数等。事实上,虽然 Redux 要求每个 action 对象都有一个 type 的字段,但是你的 reducer 逻辑不必一定要依赖它做处理。也就是说,标准方法肯定是用基于 type 的 switch 语句或者查找表。

必须将所有 state 都维护在 Redux 中吗? 可以用 React 的 setState() 方法吗?

没有 “标准”。有些用户选择将所有数据都在 Redux 中维护,那么在任何时刻,应用都是完全有序及可控的。也有人将类似于“下拉菜单是否打开”的非关键或者 UI 状态,在组件内部维护。适合自己的才是最好的。

使用局部组件状态是更好的。作为一名开发者,应该决定使用何种 state 来组装你的应用,每个 state 的生存范围是什么。在两者之间做好平衡,然后就去做吧。

这里有一些将怎样的数据放入 Redux 的经验法则:

应用的其他部分是否关心这个数据?
是否需要根据需要在原始数据的基础上创建衍生数据?
相同的数据是否被用作驱动多个组件?
能否将状态恢复到特定时间点(在时光旅行调试的时候)?
是否要缓存数据(比如:数据存在的情况下直接去使用它而不是重复去请求他)?
有许多开源组件实现了各式各样在 Redux store 存储独立组件状态的替代方法,比如 redux-ui、 redux-component、 redux-react-local等等。还可以将 Redux 的原则和 reducers 的概念应用到组件层面,按照 this.setState((previousState) => reducer(previousState, someAction)) 的情形。

为何 type 必须是字符串,或者至少可以被序列化? 为什么 action 类型应该作为常量?

和 state 一样,可序列化的 action 使得若干 Redux 的经典特性变得可能,比如时间旅行调试器、录制和重放 action。若使用 Symbol 等去定义 type 值,或者用 instanceof 对 action 做自检查都会破坏这些特性。字符串是可序列化的、自解释型,所以是更好的选择。注意,如果 action 目的是在 middleware 中处理,那么使用 Symbols、 Promises 或者其它非可序列化值也是 可以 的。 action 只有当它们正真到达 store 且被传递给 reducer 时才需要被序列化。

因为性能原因,我们无法强制序列化 action,所以 Redux 只会校验 action 是否是普通对象,以及 type 是否定义。其它的都交由你决定,但是确保数据是可序列化将对调试以及问题的重现有很大帮助。

封装并集聚公共代码是程序规划时的核心概念。虽然可以在任何地方手动创建 action 对象、手动指定 type 值,定义常量的方式使得代码的维护更为方便。如果将常量维护在单独的文件中,在 import 时校验,能避免偶然的拼写错误。

文件结构应该是什么样?项目中该如何对 action 创建函数和 reducer 分组? selector 又该放在哪里?

因为 Redux 只是数据存储的库,它没有关于工程应该被如何组织的直接主张。然后,有一些被大多数 Redux 开发者所推荐的模式:

Rails-style:“actions”、“constants”、“reducers”、“containers” 以及 “components” 分属不同的文件夹
Domain-style:为每个功能或者域创建单独的文件夹,可能会为某些文件类型创建子文件夹
“Ducks”:类似于 Domain-style,但是明确地将 action、 reducer 绑定在一起,通常将它们定义在同一文件内。
推荐做法是将 selector 与 reducer 定义在一起并输出,并在 reducer 文件中与知道 state 树真实形状的代码一起被重用(例如在 mapStateToProps 方法、异步 action 创建函数,或者 saga)。

不管代码在你的磁盘上是如何存放的,必须记住的是 action 和 reducer 不应该单独考虑。在一个文件夹中定义的 reducer 可以响应另一个文件夹中定义的 action 是非常常见的(甚至是鼓励的)。

Redux 所做的工作是什么?

Redux 所做的工作可以分为以下几部分:在 middleware 和 reducer 中处理 action (包括对象复制及不可变更新)、 action 分发之后通知订阅者、根据 state 变化更新 UI 组件。虽然在一些复杂场景下,这些都 可能 变成一个性能问题,但 Redux 本质上并没有任何慢或者低效的实现。实际上,React Redux 已经做了大量的优化工作减少不必要的重复渲染。

为何组件没有被重新渲染、或者 mapStateToProps 没有运行?

目前来看,导致组件在 action 分发后却没有被重新渲染,最常见的原因是对 state 进行了直接修改。Redux 期望 reducer 以 “不可变的方式” 更新 state,实际使用中则意味着复制数据,然后更新数据副本。如果直接返回同一对象,即使你改变了数据内容,Redux 也会认为没有变化。类似的,React Redux 会在 shouldComponentUpdate 中对新的 props 进行浅层的判等检查,以期提升性能。如果所有的引用都是相同的,则返回 false 从而跳过此次对组件的更新。

需要注意的是,不管何时更新了一个嵌套的值,都必须同时返回上层的任何数据副本给 state 树。如果数据是 state.a.b.c.d,你想更新 d,你也必须返回 c、b、a 以及 state 的拷贝。state 树变化图 展示了树的深层变化为何需要改变途经的结点。

“以不可变的方式更新数据” 并 不 代表你必须使用 Immutable.js, 虽然是很好的选择。你可以使用多种方法,达到对普通 JS 对象进行不可变更新的目的:

使用类似于 Object.assign() 或者 _.extend() 的方法复制对象, slice() 和 concat() 方法复制数组。
ES6 数组的 spread sperator(展开运算符),JavaScript 新版本提案中类似的对象展开运算符。
将不可变更新逻辑包装成简单方法的工具库。

如何在 Redux 中实现鉴权?

在任何真正的应用中,鉴权都必不可少。当考虑鉴权时须谨记:不管你怎样组织应用,都并不会改变什么,你应当像实现其它功能一样实现鉴权。这实际上很简单:

为 LOGIN_SUCCESS、LOGIN_FAILURE 等定义 action 常量。

创建接受凭证的 action 创建函数,凭证是指示身份验证成功与否的标志、一个令牌、或者作为负载的错误信息。

使用 Redux Thunk middleware 或者其它适合于触发网络请求(请求 API,如果是合法鉴权则返回令牌)的 middleware 创建一个异步的 action 创建函数。之后在本地存储中保存令牌或者给用户一个非法提示。可以通过执行上一步的 action 创建函数达到此效果。

为每个可能出现的鉴权场景(LOGIN_SUCCESS、LOGIN_FAILURE等)编写独立的 reducer。

猜你喜欢

转载自blog.csdn.net/weixin_43532346/article/details/85170244