对使用Redux和Redux-saga管理状态的思考

原文地址在个人博客, 转载请注明出处,谢谢!javascript

概述

本文介绍了对 Redux 状态管理的思想、原理、架构方法的认识和思考以及配合redux-saga处理异步操做的实践java

前言

You know, React 只是属于MV*架构模式的 view 层,是一种状态机,只使用 React 难以控制大型、复杂的应用,它须要一些框架来帮助管理状态,所以如何有效、简单、易于测试地管理这个状态机是各类架构框架感兴趣的。Facebook 早就意识到这个问题并提出了 Flux 架构,比较复杂; 后来出现了 Redux、Mobx 等。MobX 能够处理简单数据流的场景,能够实现精确更新; Redux 是从 Flux 和其余框架借鉴了一些思想, 它比 Flux 简单、易于理解、用于处理复杂数据流,并具备很强的扩展性,社区诞生了像redux-thunk、redux-promise、redux-saga等中间件用于方便地处理异步操做。最近也在项目中使用了 Redux 及其中间件 redux-saga 来管理状态和处理异步操做,这篇文章就来谈谈我对它们的思考和实践。node

正文

Redux 思想

先来谈谈背景(需求)react

我以为理解Redux的思想,谈谈MV*架构模式的思路也许会有帮助。redux

MV*架构模式,它们的核心都是职责分离、解耦,不一样的层次作不一样的事,能让一个复杂、混乱的应用变得思路清晰,代码能够复用,而且易于测试,有利于分工合做,构建更大、更复杂的应用。promise

一个应用要包括哪些功能?表现(view)、处理数据的逻辑(model)以及数据映射到表现层的逻辑(presenter or controller),数据在这三层之间流通(MVVM模式经过数据双向绑定实现view和model同步)。浏览器

React 根据state来render,它只是个状态机,并无解决管理状态的问题。咱们在单纯的使用React来写组件的时候,常常会遇到组件间通讯和管理组件state的问题,前者经常使用的解决办法就是把数据提到父组件共享;后者管理state简单的状况还行,一复杂就很麻烦且容易出错,再遇到一些须要异步处理的操做,想一想就头皮发麻。react-router

当你开发中遇到一些反人类的操做时,试着去想如何改变一下思路让它变得更简单,别耐着性子安慰本身开发就是这样 :)架构

解决方案框架

Redux 正是用来解决大型React应用所面临的状态管理、数据流通、异步处理、测试、团队合做等问题:

Redux 用单一的object tree来表示整个应用的state,这个表示state的对象树被放在惟一的store 中,state至关于store的快照;全部组件都会经过API拿到这个state,各取所需;

Redux 把页面上用户的操做或者浏览器的行为(如路由的变化)定义为一个要更新state的action,这个action是一个普通对象,它包含了要执行动做的类别和传递到state的数据(若是有的话),它只代表要更改state的意图,至关于一个信号,并不能直接修改state,Redux会集中处理这些信号,这个action由你来决定什么时候发起;

定义好信号,你还须要根据不一样的信号定义不一样的逻辑函数(reducers)来更新state。

经过这张图来整理一下:

“redux 原理图”
“redux 原理图”

咳咳...好比用户点击的一个按钮,你在按钮上绑定的回调函数调用了一个(多个)action creator,action creator就返回了一个更新state局部数据的action,store会根据这个(多个)action找到对应的reducers(reducer须要作拆分),按照action发起的顺序依次执行来更新state,每一个reducer只负责更新本身关心那部分,根 reducer 把多个子 reducer 输出合并成一个单一的 state 树,生成一个新的state保存在store中,store中的state能够经过相应API传递到子组件。

这就是整个数据流。

那Redux如何处理异步操做?

Redux借鉴了中间件思想,利用可扩展的中间件来改造dispatch函数。好比redux-thunk让dispatch不只仅能够接收action,还能够接受函数做为参数,你能够在这个函数里完成异步操做。再如redux-saga更强大、也更复杂,在后面会讲到。

Redux 架构方法

对于React技术栈,Redux实现了react-redux库来让Redux管理React应用(其余框架也有相应的库),里面集成了一些有用的函数来把一些明确的流程自动化,如createStore用于建立惟一store,能够把根reducer传进createStore使store自动调用对应reducer,能够扩展中间件;提供<Provider store>组件和connect高阶组件用来包裹render component并传递state,connect还能自动dispatch,让你只要调用action creator就能dispatch;提供combineReducers来组合分割的reducers等。

知道这些特性,就能够配合react-router构建大型应用了:

总的思路就是:利用react-router 把应用分割为各个页面,reducer、action creator也跟随页面分割而分割。每一个路由对应的页面下都有components和containers,分别存放functional components 和class components,前者用来渲染,后者当作containers被connect包裹,containers包裹components;containers从connect获得state并映射须要的数据到子组件的props,子组件再向下传递。

具体如何构建React + Redux + react-router,我在另外一篇博客里讲了。

使用这种架构,开发大型应用变得驾轻就熟。

Redux 存在的问题

可是当我深刻项目开发的时候,也逐渐发现了一些问题:

  • 这种架构项目结构不够扁平化,文件嵌套比较深,思路比较复杂,搭建、写起来比较麻烦,上手有难度;
  • 因为全部action creator都定义在页面层次上,让子组件调用必须一层一层的传递,很麻烦且很是容易出错,也很难调试;
  • state难以作到局部更新(这个能够用reselect
  • Redux只是传递了一种思路,定义了几个简单的API,很灵活,架构方式不固定,设计方式不固定(如:如何设计state树)但这也是它的缺点,新人每每看完一遍仍是不知道怎么作,对新人不友好

总之,redux能够胜任复杂数据流的应用,可是也比较难,前期架构比较麻烦,适合有经验的人。

使用redux-saga处理异步操做

Redux 倡导action 和reducer尽量"纯净",没有什么“反作用”。但是像一些异步操做好比获取数据是必须的,在哪处理这些反作用呢?redux 把这些"不纯净的"任务交给了中间件,经过 向createStore里应用中间件,在交由store处理action以前就能够对其完成一些其余的操做:

“redux 中间件”
“redux 中间件”

而redux-saga 是Redux一个强大但并不复杂的用于异步处理的中间件。

它的思路是什么?相比其余redux异步中间件如redux-thunk、redux-promise有什么不一样?

先看名字来理解:saga,这个术语经常使用于CQRS架构,表明查询与责任分离

没错,就是查询(dispatch)与责任(sagas)分离。saga提供了action监听函数,只需在组件里dispatch 相应type的action,就能够自动调用你定义好的对应这个action的异步处理函数(sagas)来完成任务,保证了只在组件里dispatch action来发起异步操做而不是redux-thunk、redux-promise的调用action creators。

另一大特点就是redux-saga作到了异步代码以同步方式写,很是直观方便,怎么作到的呢?它是利用了ES6新魔法Generator迭代器,能够完美解决异步回调地狱,让你以同步方式写异步。saga正是利用Generator特性让其处理异步变得很是方便又容易理解。这是一个常见的请求后台数据的异步操做,感觉一下:

function *fetchNodeDetailByNodeId({ payload: { nodeId } }, { call, put }) {
      try {
        const { data, status }= yield call(fetchNodeDetailByNodeId, nodeId)
        if (data && status.errmsg === 'success') {
          yield put({
            type: 'setStates',
            payload: {
              nodeDetailData: data,
            },
          });
        } else {
          message.info('开了个小差,再试一次吧..');
        }
      } catch (error) {
        console.log(error);
      }
    },复制代码

call 和 put 是saga的API,至关于dispatch,可是并非真正执行dispatch,只是发送你指定的指令,交由saga中间件来执行这个指令。这样看来,这个saga函数就是一些指令的集合,称为effects,反作用,用来描述任务

为啥要描述指令而不直接调用呢?这样是由于易于测试,若是直接调用,你还得模拟调用的函数,详见redux-saga文档。

我以为redux-saga相比于其余中间件的优势:

  • 查询与责任分离,保证了action的纯洁性,符合redux设计思想
  • 实现以同步方式写异步操做,容易理解,逻辑清晰
  • 经过发送指令而不是直接调用让异步操做变得容易测试
  • 监听、执行自动化
  • 提供了丰富强大的指令来完成复杂的操做,好比无阻塞调用,同时执行多个任务等

讲道理,任何redux异步操做均可以让saga这个中间件来完成,很是复杂的一样能够胜任,而且很容易理解(异步操做以同步方式写)和测试。再配合dva,能够减轻redux的复杂度同时完成更强大的功能。

这样以来,redux配合saga,就可让它们各司其职,整个思路也变得清晰起来:

redux 倡导action和reducer要纯洁,那就让全部异步操做这些不纯洁的任务交给saga,reducer不用变,仍是纯函数;定义好对应action的sagas专门用来处理异步操做,我只要在组件须要的地方里dispatch 纯action就好了,符合redux设计思想。

总结

使用redux来管理应用状态适用于复杂的应用,而复杂的应用会有复杂的异步处理,异步处理不要用redux的action creator,它不是用来作这个的,也违背了redux设计思想,redux把这些任务交给了异步中间件,应该由它们来完成。使用redux saga是一个推荐的选择,它懂redux,也懂你须要什么。另外,既然你用到了saga,不妨试试dva架构,5分钟上手,值得一试。

相关文章
相关标签/搜索