zoukankan      html  css  js  c++  java
  • react系列笔记:第三记-redux-saga

    github : https://github.com/redux-saga/redux-saga

    文档:https://redux-saga.js.org/  

    redux-saga:  redux中间件,旨在处理应用中的副作用

    使用:

    import createSagaMiddleware from 'redux-saga'
    import {createStore,applyMiddleware} from 'redux'
    
    const saga = createSagaMiddleware();
    
    const store = createStore(
      reducer,
      applyMiddleware(sagaMiddleware)
    )
    sagaMiddleware.run(mysagas);
      

     

    基础概念:

      saga-middleware 检查每个被 yield 的 Effect 的类型,然后决定如何实现哪个 Effect。如果 Effect 类型是 PUT 那 middleware 会 dispatch 一个 action 到 Store。 如果 Effect 类型是 CALL 那么它会调用给定的函数。

    put({type: 'INCREMENT'}) // => { PUT: {type: 'INCREMENT'} }
    call(delay, 1000)        // => { CALL: {fn: delay, args: [1000]}}

    辅助函数:

      takeEvery : 可以同时启动多次任务

      takeLatest : 在一次任务未完成之前,遇到新任务将取消之前任务。

    声明式的effect

      call:  saga通过 Generator函数实现,在yield函数后执行effect,其中call是用于执行某些异步操作的。

    yield requestSome('/xxx');
    
    yield call(requestSome,'/xxx')
    //之所以用call取代上面的直接调用写法,好处在于,
    //编写测试代码的时候,可以deepEqual  generator函数的next().value和call(requestSome,'/xxx'),这样所有的effect都可以被测试,而不需要mock yield后的执行结果

    dispatching actions:

      put : 和上面的call一样,中间件提供put 来把action丢到中间件中去dispatch,好处同样是便于测试

    高级:

    take:

      call方法可以yield一个promise,然后会阻塞generator的执行,知道promise resolve,结果返回
      take(xxx)同理,阻塞generator直到xxx匹配的action被触发

      由此:take(*)可以用于抓取log,take(*)会匹配任意的action触发事件

      使用while(true){ take(/xxx/) }  可以创建持续的action监听,当然也可以根据需求,选择性的监听,只需改版while(xxx)的条件就行了

    function* actionLog(){
      while(true){
        yield take('INCREMENT');
        console.log('do increment');
        yield take('DECREMENT');
        console.log('do decrement')
      }
    }
    //take可以控制action的监听顺序,如上,会先监听到INCREMENT之后,才会再往下监听DECREMENT,完后继续监听INCREMENT。这在一些顺序明确的action事件里,可以将流程代码写在一起,
    //如login   logout  

    fork:

      fork和take不同,take会和call一样阻塞代码的执行,知道结果返回,fork则不会,它会将任务启动并且不阻塞代码的执行,

      fork会返回一个task,可以用cacel(task)来取消任务

      https://redux-saga.js.org/docs/advanced/NonBlockingCalls.html

      此文中将login 和logout作为例子,在login请求未返回来之前执行了logout,则需要cacel未完成的login,又不能用take(login)否则阻塞logout,会在login响应之前take不到logout。

    import { take, put, call, fork, cancel } from 'redux-saga/effects'
    
    // ...
    
    function* loginFlow() {
      while (true) {
        const {user, password} = yield take('LOGIN_REQUEST')
        // fork return a Task object
        const task = yield fork(authorize, user, password)
        const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
        if (action.type === 'LOGOUT')
          yield cancel(task)
        yield call(Api.clearItem, 'token')
      }
    }
    
    
    import { take, call, put, cancelled } from 'redux-saga/effects'
    import Api from '...'
    
    function* authorize(user, password) {
      try {
        const token = yield call(Api.authorize, user, password)
        yield put({type: 'LOGIN_SUCCESS', token})
        yield call(Api.storeItem, {token})
        return token
      } catch(error) {
        yield put({type: 'LOGIN_ERROR', error})
      } finally {
        if (yield cancelled()) {
          // ... put special cancellation handling code here
        }
      }
    }

    all:

      yield表达式,可以将语句分段执行,但如果有时候想同时执行两个任务,则需要用到all

    import {all,call} from 'redux-saga/effect'
    
    //此处会同步执行两个call的任务
    const [users, repos] = yield all([
      call(fetch, '/users'),
      call(fetch, '/repos')
    ])

    race:

      和promise中的race一个概念,执行多个任务,受到响应后则继续执行  

    function* fetchPostsWithTimeout() {
      const {posts, timeout} = yield race({
        posts: call(fetchApi, '/posts'),
        timeout: call(delay, 1000)
      })
    
      if (posts)
        put({type: 'POSTS_RECEIVED', posts})
      else
        put({type: 'TIMEOUT_ERROR'})
    }

    yield * 

      通过yield * xxx()来组合多个generator任务。

    组合saga:

      可以通过yield [call(task1),call(task2)...]来组合多个generator。

    channels:

    throttle:

      节流

    delay:

      防抖动

  • 相关阅读:
    阅读<SQL语言艺术>实践五
    <SQL语言艺术>阅读计划
    文本类文件与VS关联实践
    接口开发原则
    逻辑部分开发原则
    <海量数据库解决方案>2011022301
    5800对于存储卡密码设置问题
    [转]Delphi用户登录窗口框架
    20世纪科学界最重要的12本书
    [转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching)
  • 原文地址:https://www.cnblogs.com/laojun/p/9144239.html
Copyright © 2011-2022 走看看