最后一次系统的学习 React 状态管理库 Dva,

React 状态管理 之 Dva

​ 最近在做需求的时候,需要使用到一个状态管理库来对多个组件共同状态进行统一管理,React 的状态管理库其实是有很多的,例如:最简单的 React Context ,到经典的 Redux,Mobx,再到 Hooks 时代的新起之秀:Recoil(我写的简介:https://www.codefather.cn/post/1648709562175377409)还有很多优秀的库,可以看这篇:https://juejin.cn/post/7195513281228898363#heading-27

接下来,我会按照下面思路写这篇文章:

  1. 什么时候需要使用状态管理库?
  2. 为什么选择状态管理库 Dva ?
  3. 什么是 Dva ?
  4. 怎么使用 Dva ?
  5. Dva 核心思想

本文对一些分支知识点也进行记录,大家可以自行寻找需要阅读的地方

一 ,什么时候需要使用状态管理库

image-20230517223030456

​ 因为 React 是 单向数据流(数据的流动方向总是从父组件向子组件),当我们想要多个组件共享一个状态的时候,我们就会发现我们需要一个存状态的地方,方便状态被多个组件读取,更新。这里介绍一下官方的解决方案 ,使用 Context 将它保存在更上层组件的组件中,使用 Provider 将值传递给下面的树,任何组件都可以读取它。这里它的缺点有以下几个:

  1. **组件的复用性降低:**如果一个组件依赖于某个 context,那么这个组件只能在这个 context 存在的地方使用。这可能会限制组件的复用性。
  2. **难以进行状态管理:**当需要管理的状态变得复杂时,只使用 Context 可能会变得困难。例如,如果需要管理一个全局状态,同时还需要进行异步操作,那么可能需要引入额外的库(如 Redux 或 MobX)。
  3. **性能问题:**当一个 Context 值改变时,所有消费这个 Context 的组件都将重新渲染,无论这个改变是否会影响到它们。如果有大量的组件订阅了同一个 Context,可能会引发性能问题。
  4. **代码结构问题:**过度使用 Context 可能会导致代码结构混乱。有时候很难确定哪个部分的代码应该放在 Context Provider 中,哪个部分的代码应该放在 Context Consumer 中。
  5. **不易于调试:**与 Redux 相比,Context API 的调试工具不够强大。如果状态传递链很长或者很复杂,问题定位和调试可能会比较困难。

这些问题在面对复杂状态管理,异步状态更新的情况下,会变得极为棘手,于是社区就产生了很多优秀的状态管理库来解决问题

​ 我们还会在

  • 复杂的用户交互:例如多个视图之间的跳转,多个组件之间的交互等,使用状态管理库(提取 UI 交互控制状态)可以帮助你更好地管理这些交互。
  • **大型项目:**对于大型的、复杂的项目,状态管理库可以帮助你更好地组织代码,使代码更易于理解和维护
  • **异步操作:**如果你的应用需要处理很多异步操作,例如网络请求,状态管理库可以帮助你更好地处理这些操作。
  • **跨组件通信:**如果你的组件树结构复杂,父子组件或者兄弟组件之间需要频繁通信,那么使用状态管理库可以简化这种跨组件的通信。

重点:并不是所有的 React 项目都需要使用状态管理库。对于一些小型的、简单的项目,React 自带的状态管理(useState 和 useReducer hooks)可能已经足够了。引入过度复杂的状态管理库可能会导致代码变得更复杂,更难以理解和维护。引入复杂的状态管理库是为了解决

  1. 更加复杂的逻辑,
  2. 简化代码,
  3. 便于代码维护
  4. 提高代码可读性

而不是为了让代码变得更糟糕

为什么选择状态管理库 Dva ?

​ 我在开发的项目是基于 umi 3 搭建的,所以如果选择状态管理工具,那一定就是自带的@umijs/plugin-dva 插件,如果我的项目是 next.js 搭建的,我应该会选择 Recoil ,因为它的代码更加简洁,方便阅读。

​ 不过,Dva也是一个很优秀的状态管理库,尤其是在大型项目中,它强制的规范了代码的书写方式,这样代码更加方便维护,因为规范代码在多人开发的大型项目是极为重要的,因为每个程序员都有自己的偏好代码书写方式,

​ 这里,还有一个问题,就是 umi 3 是有两个内置的状态管理插件的 ,分别是刚刚提到的@umijs/plugin-dva 和 更加轻量的@umijs/plugin-model 。那如何选择呢?

  1. **@umijs/plugin-model:**这是 Umi 3 中的一个新特性,是一种基于 hooks 的轻量级全局状态管理方案。plugin-model 以文件为单位进行状态管理,对于每个 model 文件,内部会自动创建一个独立的 React context。并且在使用时,不需要 dispatch,直接使用 actions 即可。最佳实践:@umijs/plugin-model 更适合在项目中快速、轻量级地管理全局状态。它没有繁琐的概念,API 极简,适合小到中型项目。此外,它基于 React hooks,如果你的项目已经或者准备使用 React hooks,@umijs/plugin-model 会是一个不错的选择。
  2. **@umijs/plugin-dva:**这是将 DVA 集成到 Umi 3 中的插件,使用了 Redux、Redux-saga、React-router 等技术进行状态管理。DVA 是一种更完整的状态管理方案,包含了 action、reducer、effects 等概念。最佳实践:@umijs/plugin-dva 更适合在大型复杂项目中进行状态管理。它有完善的概念和方法,可以处理复杂的异步操作和副作用。如果你的项目中有复杂的状态管理需求,或者项目团队已经习惯了 Redux 的开发模式,那么 @umijs/plugin-dva 会是一个更好的选择。

总的来说,@umijs/plugin-model@umijs/plugin-dva 的主要区别在于其复杂度和应用场景。plugin-model 更简单、更轻量级,适合小到中型项目,它也更加自由,代码更加不可控,而 plugin-dva 更复杂、更强大,适合大型复杂项目,它的约束更多,代码更加规范。

什么是 Dva ?

​ 我认为 Dva 最好的介绍文档,就是它的官网:https://dvajs.com/ ,它是由 蚂蚁 的前端大佬 sorrycc 写的,

​ dva 首先是一个基于 reduxredux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-routerfetch,所以也可以理解为一个轻量级的应用框架

​ 我觉得Dva 产生的原因有 3 个,

  1. 简化 Redux 的使用:Redux 是一个非常强大的状态管理工具,但是它的使用相对复杂,需要写很多模板代码。例如,你需要定义 actions,reducers,然后再将它们关联起来。DVA 对 Redux 进行了封装,让开发者可以更简单地使用 Redux。
  2. 处理异步操作:Redux 本身并不支持异步操作,通常需要使用中间件如 Redux-thunk 或 Redux-saga 来处理。DVA 内置了 Redux-saga,使得处理异步操作变得更简单
  3. 集成路由:在传统的 React + Redux 应用中,路由和状态管理通常是分开的。DVA 将 React-router 集成进来,使得路由和状态管理可以一体化处理。

当然,它还提供了 插件系统,遵循 Elm 架构,

这里讲一下 Elm 架构

Elm 架构是一种用于构建前端应用的架构模式,它起源于 Elm 语言,但已被许多其他的前端框架和库所采纳,包括 Redux 和 DVA。

Elm 架构主要包含以下几个部分:

  1. Model:模型代表了应用的状态。在一个计数器应用中,模型可能就是一个数字;在一个待办事项列表应用中,模型可能是一个代表待办事项的数组。
  2. Update:更新函数定义了如何根据接收到的 action 更新模型。它接收当前的模型和一个 action,然后返回一个新的模型。这个过程是纯函数,即同样的输入总会得到同样的输出,没有副作用。
  3. View:视图函数负责根据模型生成 UI。它接收模型作为参数,然后返回一些描述 UI 的代码(在 Web 开发中,通常是 HTML 或 JSX)。
  4. Actions:动作描述了用户或系统可能对应用做的操作。例如,在一个计数器应用中,可能有 "增加" 和 "减少" 两种操作。

​ 在 Elm 架构中,数据流是单向的:用户或系统通过触发动作来修改模型,然后更新函数根据动作和当前模型计算出新的模型,最后视图函数使用新的模型生成新的 UI。

​ 这种单向数据流使得应用的状态变化变得可预测和易于理解,也更便于调试和测试。因此,Elm 架构已经被广泛应用于前端开发中。

怎么使用 Dva ?

参考链接:https://dvajs.com/guide/getting-started.html

​ 我觉得最好学习 Dva 的方式就是自己写一个小 demo,例如:写一个需要状态共享,代码逻辑可以提取的小系统,先不使用任何状态管理库去实现,然后再用 Dva 实现,你会深刻体会到它的作用,它解决了什么问题。最好通过这个demo体验它的所有功能,然后再去看 Dva的概念,加深对状态管理库 Dva 思想的理解

Dva 核心思想

参考链接:https://dvajs.com/guide/concepts.html

img

​ 在 DVA 中,所有的数据流都遵循同样的模式:首先,用户交互或者其他事件触发 action;然后,saga 中间件捕获这个 action,并可能触发异步操作,如数据请求;最后,根据 action 的类型,对应的 reducer 将会更新状态。

​ DVA 还引入了 Model 的概念,将 Redux 中的 reducers、effects 和 subscriptions 集合在一起,使得相关的代码更加集中和模块化。通过这种方式,DVA 使得状态管理的逻辑更加清晰,更易于理解和维护。

​ 此外,DVA 还内置了路由管理功能,提供了基于路由的动态加载机制,使得大型应用的状态管理和路由管理可以统一处理。

注意事项 & 经验

  1. 合理设计 Model:在 DVA 中,Model 是核心的部分,对应的就是你的数据模型。一个好的 Model 设计应该是简单并且易于理解的,避免过度复杂的状态设计。
  2. 保持 Reducer 的纯净:Reducer 是一个纯函数,它不应该产生任何副作用。在 Reducer 中,你不能直接修改传入的 state,而是应该返回一个新的 state,这样也符合React 的不变性,但是如果你觉得麻烦,可以通过
  3. 尽量减少 state 的复杂性:尽量保持 state 的扁平化,避免过度的嵌套结构。这样可以使 state 更容易理解和操作。
  4. 善用 Effects:Effects 是 DVA 中处理异步操作的地方。你可以在这里进行各种异步操作,如获取数据、延时操作等。
  5. 合理使用 Subscription:Subscription 可以用来监听数据源并根据需要 dispatch action,但并不是所有的 action 都需要通过 Subscription 来触发。在某些情况下,直接在组件中 dispatch action 会更加方便和直观。
  6. 注意性能优化:在编写代码时,你应该注意避免不必要的渲染和状态更新,以提高应用的性能。
  7. 遵循单一职责原则:每个 Model 应该只负责管理一部分状态,每个 Component 应该只负责渲染一部分 UI。这样可以使你的代码更加模块化,更易于维护。
  8. 编写可测试的代码:尽量让你的代码更容易进行单元测试。例如,你可以将业务逻辑尽可能抽取出来,使其不依赖于特定的组件或者环境。
  9. 直接在 model 中监听 state 的变化可能会导致逻辑混乱,不易于维护,因此并不推荐这样做。
  10. 在大多数情况下,你应该尽量在组件中监听 state 的变化,然后 dispatch action 来更新 state。
  11. 我们应该把 更新状态 state 和 同步操作 都在 Reducer 中实现,把异步事件,接口请求都在 Effect 中实现,
  12. 我们可以在 Subscription 中进行订阅数据源,数据源可以是
  • 当前的时间
  • 服务器的 websocket 连接
  • keyboard 输入
  • geolocation (用户的地理位置信息的变化)变化
  • history 路由变化等等。

当数据源发生变化时,可以通过 dispatch 触发相应的 action,进而改变应用状态。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
luckySnail
下载 APP