• redux-saga入门


    redux-sage和redux-thunk类似都是redux的中间件,都用于处理异步操作。redux-saga使用ES6的Generator功能,避免了redux-thunk的回调写法,并且便于测试。

    下面展示了最简单是使用示例

    import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
    import Api from '...'
    
    // worker Saga : 将在 USER_FETCH_REQUESTED action 被 dispatch 时调用
    function* fetchUser(action) {
       try {
          const user = yield call(Api.fetchUser, action.payload.userId);
          yield put({type: "USER_FETCH_SUCCEEDED", user: user});
       } catch (e) {
          yield put({type: "USER_FETCH_FAILED", message: e.message});
       }
    }
    
    /*
      在每个 `USER_FETCH_REQUESTED` action 被 dispatch 时调用 fetchUser
      允许并发(译注:即同时处理多个相同的 action)
    */
    function* mySaga() {
      yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
    }
    
    /*
      也可以使用 takeLatest
    
      不允许并发,dispatch 一个 `USER_FETCH_REQUESTED` action 时,
      如果在这之前已经有一个 `USER_FETCH_REQUESTED` action 在处理中,
      那么处理中的 action 会被取消,只会执行当前的
    */
    function* mySaga() {
      yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
    }
    
    export default mySaga;
    import { createStore, applyMiddleware } from 'redux'
    import createSagaMiddleware from 'redux-saga'
    
    import reducer from './reducers'
    import mySaga from './sagas'
    
    // create the saga middleware
    const sagaMiddleware = createSagaMiddleware()
    // mount it on the Store
    const store = createStore(
      reducer,
      applyMiddleware(sagaMiddleware)
    )
    
    // then run the saga
    sagaMiddleware.run(mySaga)
    
    // render the application

    put等一些方法是saga提供的指令,返回一个Effect,Effect是一个简单的js对象,包含了要被sgag middleware执行的指令,当middleware拿到一个被saga yield的Effect,它会暂停saga,直到Effect执行完成,然后saga回恢复执行。

    redux-saga提供一些辅助函数,用于在action被发起到store是派生人物。

    takeEvery()可以在某个action发起是启动一个任务

    import { takeEvery } from 'redux-saga'
    
    function* watchFetchData() {
      yield* takeEvery('FETCH_REQUESTED', fetchData)
    }

    takeLatest()和takeEvent()功能类似,不同的是takeEvent()允许同时启动多个任务,即使已启动的任务未结束,也会启动一个新的任务。takeLatest()同时只允许启动一个任务,一个新的任务启动,之前的任务即使未完成也会被取消。

    如果有多个sage监听不同的action

    import { takeEvery } from 'redux-saga/effects'
    
    // FETCH_USERS
    function* fetchUsers(action) { ... }
    
    // CREATE_USER
    function* createUser(action) { ... }
    
    // 同时使用它们
    export default function* rootSaga() {
      yield takeEvery('FETCH_USERS', fetchUsers)
      yield takeEvery('CREATE_USER', createUser)
    }

    在 Generator 函数中,yield 右边的任何表达式都会被求值,结果会被 yield 给调用者。

    测试时我我们不可能真正的执行任务发到服务器,我们需要检测的只是yield后面语句调用(通常是调用参数的检查)或值是否正确。但当yield 一个返回值是Promise的方法时,就没办法比较了,所以当yield一个异步api时使用saga提供的call()方法代替异步api的直接调用,call()返回一个普通的js对象,所以在测试时可以很容易检测。

    import { call } from 'redux-saga/effects'
    
    function* fetchProducts() {
      const products = yield call(Api.fetch, '/products')
      // ...
    }
    import { call } from 'redux-saga/effects'
    import Api from '...'
    
    const iterator = fetchProducts()
    
    // expects a call instruction
    assert.deepEqual(
      iterator.next().value,
      call(Api.fetch, '/products'),
      "fetchProducts should yield an Effect call(Api.fetch, './products')"
    )

    当dispatch一个action时,也同样难以测试,所以saga提供了put()方法来代替直接调用dispatch。

    import { call, put } from 'redux-saga/effects'
    //...
    
    function* fetchProducts() {
      const products = yield call(Api.fetch, '/products')
      // 创建并 yield 一个 dispatch Effect
      yield put({ type: 'PRODUCTS_RECEIVED', products })
    }
    import { call, put } from 'redux-saga/effects'
    import Api from '...'
    
    const iterator = fetchProducts()
    
    // 期望一个 call 指令
    assert.deepEqual(
      iterator.next().value,
      call(Api.fetch, '/products'),
      "fetchProducts should yield an Effect call(Api.fetch, './products')"
    )
    
    // 创建一个假的响应对象
    const products = {}
    
    // 期望一个 dispatch 指令
    assert.deepEqual(
      iterator.next(products).value,
      put({ type: 'PRODUCTS_RECEIVED', products }),
      "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
    )

    错误处理

    使用try...catch来处理错误

    import Api from './path/to/api'
    import { call, put } from 'redux-saga/effects'
    
    // ...
    
    function* fetchProducts() {
      try {
        const products = yield call(Api.fetch, '/products')
        yield put({ type: 'PRODUCTS_RECEIVED', products })
      }
      catch(error) {
        yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
      }
    }

    在测试时使用throw来抛出一个错误

    import { call, put } from 'redux-saga/effects'
    import Api from '...'
    
    const iterator = fetchProducts()
    
    // 期望一个 call 指令
    assert.deepEqual(
      iterator.next().value,
      call(Api.fetch, '/products'),
      "fetchProducts should yield an Effect call(Api.fetch, './products')"
    )
    
    // 创建一个模拟的 error 对象
    const error = {}
    
    // 期望一个 dispatch 指令
    assert.deepEqual(
      iterator.throw(error).value,
      put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
      "fetchProducts should yield an Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })"
    )
  • 相关阅读:
    [其他]将Windows Terminal添加到右键菜单
    [VS Code]在自己的Ubuntu服务器上构建VSCode Online
    [Go]goFileView-基于Golang的在线Office全家桶预览
    [Go]基于Go语言的Web路由转发,多个网站共享一个端口(新版本,支持WebSocket)
    [WSL]在Windows10子系统里安装运行桌面(xUbuntu)
    [Go]使用Golang对鸢尾花数据集进行k-means聚类
    [Python+JavaScript]JS调用摄像头并拍照,上传至tornado后端并转换为PIL的Image
    [Python]Python基于OpenCV批量提取视频中的人脸并保存
    [WSL]Windows10 Ubuntu子系统编译安装线程安全版LAMP
    [Go]基于Go语言的Web路由转发,多个网站共享一个端口(存在问题,已经抛弃,新解决方案请看新博客)
  • 原文地址:https://www.cnblogs.com/ssw-men/p/11477386.html
Copyright © 2020-2023  润新知