Redux 常见问题解答:操作
目录
操作
为什么 type
应该是一个字符串?为什么我的操作类型应该是常量?
与状态一样,可序列化动作使 Redux 的一些定义功能成为可能,例如时间旅行调试以及记录和重放动作。使用类似于 `Symbol` 的 `type` 值或使用 `instanceof` 检查动作本身会破坏这一点。字符串是可序列化的,并且易于自描述,因此是更好的选择。请注意,如果动作旨在供中间件使用,则在动作中使用 `Symbol`、`Promise` 或其他不可序列化值是可以的。动作只需要在实际到达存储并传递给 reducer 时才可序列化。
由于性能原因,我们无法可靠地强制执行可序列化动作,因此 Redux 只检查每个动作是否为普通对象,以及 `type` 是否为字符串。其余部分由您决定,但您可能会发现保持所有内容可序列化有助于调试和重现问题。
封装和集中化常用代码片段是编程中的一个关键概念。虽然在任何地方手动创建动作对象以及手动编写每个 `type` 值当然是可以的,但定义可重用常量可以使代码维护更容易。如果您将常量放在单独的文件中,您可以 检查您的 `import` 语句是否存在拼写错误,这样您就不会意外使用错误的字符串。
更多信息
文档
讨论
- #384:建议动作常量以过去时命名
- #628:使用更少样板代码创建简单动作的解决方案
- #1024:建议:声明式 reducer
- #1167:没有 switch 的 reducer
- Stack Overflow:为什么 Redux 中需要“动作”作为数据?
- Stack Overflow:Redux 中常量的作用是什么?
reducer 和动作之间总是存在一对一映射吗?
不,我们建议您编写独立的小型 reducer 函数,每个函数负责更新状态的特定切片。我们称这种模式为“reducer 组合”。给定的操作可以由所有、部分或没有 reducer 函数处理。这使组件与实际数据更改脱钩,因为一个操作可能会影响状态树的不同部分,并且组件无需了解这一点。一些用户选择将它们更紧密地绑定在一起,例如“ducks”文件结构,但默认情况下绝对没有一对一的映射,并且您应该在任何时候想要在多个 reducer 中处理操作时摆脱这种范式。
更多信息
文档
讨论
- Twitter:最常见的 Redux 误解
- #1167:没有 switch 的 reducer
- Reduxible #8:reducer 和 action creator 不是一对一的映射
- Stack Overflow:我可以不使用 Redux Thunk 中间件就调度多个操作吗?
如何表示“副作用”,例如 AJAX 调用?为什么我们需要像“action creator”、“thunk”和“中间件”这样的东西来执行异步行为?
这是一个漫长而复杂的话题,关于如何组织代码以及应该使用哪些方法,存在着各种各样的观点。
任何有意义的 Web 应用程序都需要执行复杂的逻辑,通常包括异步工作,例如发出 AJAX 请求。该代码不再仅仅是其输入的函数,与外部世界的交互被称为“副作用”
Redux 受到函数式编程的启发,并且开箱即用,没有地方执行副作用。特别是,reducer 函数必须始终是(state, action) => newState
的纯函数。但是,Redux 的中间件使得拦截已分派的 action 并围绕它们添加额外的复杂行为成为可能,包括副作用。
一般来说,Redux 建议带有副作用的代码应该是 action 创建过程的一部分。虽然该逻辑可以在 UI 组件内部执行,但通常将该逻辑提取到可重用的函数中是有意义的,这样就可以从多个地方调用相同的逻辑——换句话说,一个 action creator 函数。
最简单、最常见的做法是添加 Redux Thunk 中间件,它允许您编写具有更复杂异步逻辑的动作创建器。另一种广泛使用的方法是 Redux Saga,它允许您使用生成器编写更像同步的代码,并且可以像 Redux 应用程序中的“后台线程”或“守护进程”一样工作。另一种方法是 Redux Loop,它通过允许您的 reducer 声明对状态更改的副作用并单独执行它们来反转该过程。除此之外,还有 *许多* 其他社区开发的库和想法,每个库和想法都有自己关于如何管理副作用的看法。
更多信息
文档
文章
讨论
- #291:尝试将 API 调用放在正确的位置
- #455:建模副作用
- #533:异步动作创建器的更简单介绍
- #569:提案:显式副作用的 API
- #1139:基于生成器和传奇的替代副作用模型
- Stack Overflow:为什么我们需要中间件来进行 Redux 中的异步流程?
- Stack Overflow:如何在 Redux 中调度带有超时的动作?
- Stack Overflow:我应该将与 Redux 中的动作相关的同步副作用放在哪里?
- Stack Overflow:如何在 Redux 中处理复杂的副作用?
- Stack Overflow:如何对异步 Redux 操作进行单元测试以模拟 ajax 响应?
- Stack Overflow:如何响应 Redux 中的状态更改来触发 AJAX 调用?
- Reddit:帮助使用 Redux-Promise 中间件执行异步 API 调用。
- Twitter:传奇、循环和其他方法之间的可能比较
我应该使用哪个异步中间件?您如何决定在 thunk、传奇、可观察对象或其他东西之间进行选择?
有 许多可用的异步/副作用中间件,但最常用的中间件是 redux-thunk
、redux-saga
和 redux-observable
。这些是不同的工具,具有不同的优势、劣势和用例。
一般来说
- Thunks 最适合处理复杂的同步逻辑(尤其是需要访问整个 Redux 存储状态的代码)和简单的异步逻辑(例如基本的 AJAX 调用)。使用 `async/await`,对于一些更复杂的基于 Promise 的逻辑,使用 thunks 也是合理的。
- Sagas 最适合处理复杂的异步逻辑和解耦的“后台线程”型行为,尤其是在需要监听分派的 action(这是 thunks 做不到的)时。它们需要熟悉生成器函数和 `redux-saga` 的“效果”操作符。
- Observables 解决与 sagas 相同的问题,但依赖于 RxJS 来实现异步行为。它们需要熟悉 RxJS API。
我们建议大多数 Redux 用户应该从 thunks 开始,如果他们的应用程序确实需要处理更复杂的异步逻辑,则可以稍后添加额外的副作用库,例如 sagas 或 observables。
由于 sagas 和 observables 具有相同的用例,应用程序通常会使用其中一个,而不是两者都使用。但是,请注意,**将 thunks 与 sagas 或 observables 结合使用绝对没问题**,因为它们解决的是不同的问题。
文章
讨论
- Reddit:关于将 thunks 和 sagas 结合使用的讨论,以及 sagas 的优缺点
- Stack Overflow:使用 ES2015 生成器与 redux-saga 与使用 ES2017 async/await 与 redux-thunk 的优缺点
- Stack Overflow:为什么使用 Redux-Observable 而不是 Redux-Saga?
我应该从一个 action creator 中连续分派多个 action 吗?
对于如何构建 action 没有特定的规则。使用 Redux Thunk 之类的异步中间件确实可以实现以下场景,例如连续分派多个不同的但相关的 action,分派 action 来表示 AJAX 请求的进度,根据状态有条件地分派 action,甚至分派 action 并立即检查更新后的状态。
一般来说,请询问这些操作是否相关但独立,或者是否应该实际表示为一个操作。根据您的实际情况做出最合理的决定,但请尽量平衡 reducer 的可读性和操作日志的可读性。例如,包含整个新状态树的操作会使您的 reducer 变成一行代码,但缺点是您将无法了解更改发生的原因,因此调试变得非常困难。另一方面,如果您在循环中发出操作以保持它们粒度,则表明您可能需要引入一种以不同方式处理的新操作类型。
在您关心性能的地方,请尽量避免在同一时间内连续多次分派。有一些插件和方法可以批量分派操作。
更多信息
文档
文章
讨论