React Native Redux Typescript使用 redux-thunk or redux-saga

Redux 的核心理念是严格的单向数据流,只能通过 dispatch(action) 的方式修改 store,流程如下:

view ->  action -> reducer -> store

但是在业务复杂的以及和api数据对接的过程中肯定会遇到大量的异步操作。我们如何来解决这些场景呢?

redux中间件

什么是redux中间件

redux中间件

这里我们先从redux的中间件说起, 中间件,顾名思义:进行中间处理的物件。类似于面向对象编程的AOP编程思想(不了解AOP的可以忽略这句话)

简单的说:中间件可以控制在store dispatch action之前和之后的业务逻辑。也就是说 中间件实现了改写 store.dispatch 方法实现了action -> reducer的拦截的行为。

如果我们分别注册三个中间件: 中间件A 中间件B 中间件C

那么

中间件A -> 中间件B-> 中间件C-> 原始 dispatch -> 中间件C -> 中间件B -> 中间件A

和异步处理的关系

综上所述:中间件可以领过的改变 dispatch的时机,这样我们就可以很方便的处理异步场景了。

因此各种 redux异步处理中间件应运而生。比较知名的有redux-thunk和redux-saga。

redux-thunk

redux-thunk中间件可以让action创建函数先不返回一个action对象,而是返回一个函数,函数传递两个参数(dispatch,getState),在函数体内进行业务逻辑的封装

functionadd(){return{type:'ADD',    }}functionaddIfOdd(){return(dispatch, getState) =>{constcurrentValue = getState();if(currentValue %2==0) {returnfalse;        }//分发一个任务dispatch(add())    }}

详细代码可以查看分支:https://github.com/YahuiWong/react-native-typescript/tree/redux-thunk

redux-saga

sages 采用 Generator 函数来 yield Effects(包含指令的文本对象)。Generator 函数的作用是可以暂停执行,再次执行的时候从上次暂停的地方继续执行。Effect 是一个简单的对象,该对象包含了一些给 middleware 解释执行的信息。你可以通过使用 effects API 如 fork,call,take,put,cancel 等来创建 Effect。( redux-saga API 参考)

如 yield call(fetch, '/products') 即 yield 了下面的对象,call 创建了一条描述结果的信息,然后,redux-saga middleware 将确保执行这些指令并将指令的结果返回给 Generator:

// Effect -> 调用 fetch 函数并传递 `./products` 作为参数{type: CALL,function: fetch,  args: ['./products']}

与 redux-thunk 不同的是,在 redux-saga 中,UI 组件自身从来不会触发任务,它们总是会 dispatch 一个 action 来通知在 UI 中哪些地方发生了改变,而不需要对 action 进行修改。redux-saga 将异步任务进行了集中处理,且方便测试。

dispacth({ type: 'FETCH_REQUEST', url: /* ... */} );

所有的东西都必须被封装在 sagas 中。sagas 包含3个部分,用于联合执行任务:

worker saga

做所有的工作,如调用 API,进行异步请求,并且获得返回结果

watcher saga

监听被 dispatch 的 actions,当接收到 action 或者知道其被触发时,调用 worker saga 执行任务

root saga

立即启动 sagas 的唯一入口

☀ 如何使用?

首先,我们得在文件入口中加入 saga 中间件,并且启动它,它会一直运行:

//...import{ createStore, applyMiddleware}from'redux';importcreateSagaMiddlewarefrom'redux-saga';importappReducerfrom'./reducers';importrootSagafrom'./saga';//...constsagaMiddleware = createSagaMiddleware()conststore=createStore(rootReducer,applyMiddleware(sagaMiddleware));sagaMiddleware.run(rootSaga)render(,document.getElementById('app'));

然后,就可以在 sagas 文件夹中集中写 saga 文件了:

import{delay}from'redux-saga';import{put,takeEvery,all}from'redux-saga/effects';import{ADD}from'./actionsTypes';function*addSync(){yielddelay(1000);yieldput({type:ADD})}function*watchaddSync(){yieldtakeEvery("addSync",addSync)}exportdefaultfunction*rootSaga(){yieldall([        watchaddSync()    ])}

在 redux-saga 中的基本概念就是:sagas 自身不真正执行副作用(如函数 call),但是会构造一个需要执行作用的描述。中间件会执行该副作用并把结果返回给 generator 函数。

对上述例子的说明:

(1)引入的 redux-saga/effects 都是纯函数,每个函数构造一个特殊的对象,其中包含着中间件需要执行的指令,如:call(fetchUrl, url) 返回一个类似于 {type: CALL, function: fetchUrl, args: [url]} 的对象。

(2)在 watcher saga watchFetchRequests中:

首先 yield take('FETCH_REQUEST') 来告诉中间件我们正在等待一个类型为 FETCH_REQUEST 的 action,然后中间件会暂停执行 wacthFetchRequests generator 函数,直到 FETCH_REQUEST action 被 dispatch。一旦我们获得了匹配的 action,中间件就会恢复执行 generator 函数。

下一条指令 fork(fetchUrl, action.url) 告诉中间件去无阻塞调用一个新的 fetchUrl 任务,action.url 作为 fetchUrl 函数的参数传递。中间件会触发 fetchUrl generator 并且不会阻塞 watchFetchRequests。当fetchUrl 开始执行的时候,watchFetchRequests 会继续监听其它的 watchFetchRequests actions。当然,JavaScript 是单线程的,redux-saga 让事情看起来是同时进行的。

(3)在 worker saga fetchUrl 中,call(fetch,url) 指示中间件去调用 fetch 函数,同时,会阻塞fetchUrl 的执行,中间件会停止 generator 函数,直到 fetch 返回的 Promise 被 resolved(或 rejected),然后才恢复执行 generator 函数。

最后,总结一下 redux-saga 的优点:

(1)声明式 Effects:所有的操作以JavaScript对象的方式被 yield,并被 middleware 执行。使得在 saga 内部测试变得更加容易,可以通过简单地遍历 Generator 并在 yield 后的成功值上面做一个 deepEqual 测试。

(2)高级的异步控制流以及并发管理:可以使用简单的同步方式描述异步流,并通过 fork 实现并发任务。

(3)架构上的优势:将所有的异步流程控制都移入到了 sagas,UI 组件不用执行业务逻辑,只需 dispatch action 就行,增强组件复用性。

详细代码可以查看分支:https://github.com/YahuiWong/react-native-typescript/tree/redux-saga 如果觉得有用,请Star ,谢谢!

参考:

https://segmentfault.com/a/1190000007248878#articleHeader7

你可能感兴趣的:(React Native Redux Typescript使用 redux-thunk or redux-saga)