• Redux-Saga.js 学习笔记


    昨天认真看完了Promise 与 Generator,今天早上认真学了一下redux-saga.js。把笔记输出到这里

    redux-saga的api

    • put

      作用类似于 dispatch,用来 put 同步 的 generator function,也把一些状态 put 到 reducer

    • call

      我的理解是 调用api , 发起请求,也可以用来call 同步的 action

      官方说明

      与前面的例子不同的是,现在我们不立即执行异步调用,相反,call 创建了一条描述结果的信息。 就像在 Redux 里你使用 action 创建器,创建一个将被 Store 执行的、描述 action 的纯文本对象。 call 创建一个纯文本对象描述函数调用。redux-saga middleware 确保执行函数调用并在响应被 resolve 时恢复 generator。

      需要注意的,写异步流的时候

      // 错误写法,effects 将按照顺序执行
      const users = yield call(fetch, '/users'),
            repos = yield call(fetch, '/repos')
      
      // 由于第二个 effect 将会在第一个 call 执行完毕才开始。所以我们需要这样写:
      import { call } from 'redux-saga/effects'
      
      // 正确写法, effects 将会同步执行
      const [users, repos] = yield [
        call(fetch, '/users'),
        call(fetch, '/repos')
      ]
      
      
    • takeEvery

      非常常见,提供了类似 redux-thunk的行为

      // 首先我们创建一个将执行异步 action 的任务:
      import { call, put } from 'redux-saga/effects'
      
      export function* fetchData(action) {
         try {
            const data = yield call(Api.fetchUser, action.payload.url);
            yield put({type: "FETCH_SUCCEEDED", data});
         } catch (error) {
            yield put({type: "FETCH_FAILED", error});
         }
      }
      // 然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务。
      import { takeEvery } from 'redux-saga'
      
      function* watchFetchData() {
        yield* takeEvery('FETCH_REQUESTED', fetchData)
      }
      

      在上面的例子中,takeEvery 允许多个 fetchData 实例同时启动。在某个特定时刻,尽管之前还有一个或多个 fetchData 尚未结束,我们还是可以启动一个新的 fetchData 任务,

    • takeLatest

      如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据)。我们可以使用 takeLatest 辅助函数。

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

      takeEvery 不同,在任何时刻 takeLatest 只允许一个 fetchData 任务在执行。并且这个任务是最后被启动的那个。 如果已经有一个任务在执行的时候启动另一个 fetchData ,那之前的这个任务会被自动取消。

    • select

      根据 在组件中dispatch一个action的例子中,如果要在effects中对于param数据和当前的state数据进行再出处理,这里怎么获取state呢?采用select,如下:

      export default {
       
        namespace: 'example',   
       
        state: {num:1},     //表示当前的example中的state状态,这里可以给初始值,这里num初始为1
       
       
        effects: { //这里是做异步处理的
          *addByONe({ param}, { call, put,select }) { //这里使用select
       
            const num = yield select(state => state.num)    //这里就获取到了当前state中的数据num
           //方式二: const num = yield select(({num}) =>num)
       
          //方式三: const num = yield select(_ =>_.num)
       
            let param1;
             param1 = num + param;   这里就可以使用num进行操作了
        
            yield put({
               type: 'save',   
               num:param1
            });
          }
         
       
        },
          
        //用来保存更新state值 上面的put方法调用这里的方法
        reducers: {
          save(state, action) { //这里的state是当前总的state,这里的action包含了上面传递的参数和type
            return { ...state, ...action.num }; //这里用ES6语法来更新当前state中num的值
          },
        },
       
      };
      
    • take

      take 就像我们更早之前看到的 callput。它创建另一个命令对象,告诉 middleware 等待一个特定的 action。 正如在 call Effect 的情况中,middleware 会暂停 Generator,直到返回的 Promise 被 resolve。 在 take 的情况中,它将会暂停 Generator 直到一个匹配的 action 被发起了。 在以上的例子中,watchAndLog 处于暂停状态,直到任意的一个 action 被发起。

      import { select, take } from 'redux-saga/effects'
      
      function* watchAndLog() {
        while (true) {
          const action = yield take('*')
          const state = yield select()
      
          console.log('action', action)
          console.log('state after', state)
        }
      }
      

      一个简单的例子,假设在我们的 Todo 应用中,我们希望监听用户的操作,并在用户初次创建完三条 Todo 信息时显示祝贺信息。

      import { take, put } from 'redux-saga/effects'
      
      function* watchFirstThreeTodosCreation() {
        for (let i = 0; i < 3; i++) {
          const action = yield take('TODO_CREATED')
        }
        yield put({type: 'SHOW_CONGRATULATION'})
      }
      

      使用拉取(pull)模式,我们可以在同一个地方写控制流,而不是重复处理相同的 action。

      function* loginFlow() {
        while (true) {
          yield take('LOGIN')
          // ... perform the login logic
          yield take('LOGOUT')
          // ... perform the logout logic
        }
      }
      
    • fork

      为了表示无阻塞调用,redux-saga 提供了另一个 Effect:fork。 当我们 fork 一个 任务,任务会在后台启动,调用者也可以继续它自己的流程,而不用等待被 fork 的任务结束。

      所以为了让 loginFlow 不错过一个并发的 LOGOUT,我们不应该使用 call 调用 authorize 任务,而应该使用 fork

      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
          }
        }
      }
      
    • cancel

      如果我们收到一个 LOGOUT action,我们将那个 task 传入给 cancel Effect。 如果任务仍在运行,它会被中止。如果任务已完成,那什么也不会发生,取消操作将会是一个空操作(no-op)。最后,如果该任务完成了但是有错误, 那我们什么也没做,因为我们知道,任务已经完成了。

      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
          }
        }
      }
      
    • race

      有时候我们同时启动多个任务,但又不想等待所有任务完成,我们只希望拿到 胜利者:即第一个被 resolve(或 reject)的任务。 race Effect 提供了一个方法,在多个 Effects 之间触发一个竞赛(race)。

      // 下面的示例演示了触发一个远程的获取请求,并且限制了 1 秒内响应,否则作超时处理。
      import { race, call, put } from 'redux-saga/effects'
      import { delay } from 'redux-saga'
      
      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'})
      }
      

      race 的另一个有用的功能是,它会自动取消那些失败的 Effects。例如,假设我们有 2 个 UI 按钮:

      • 第一个用于在后台启动一个任务,这个任务运行在一个无限循环的 while(true) 中(例如:每 x 秒钟从服务器上同步一些数据)
      • 一旦该后台任务启动了,我们启用第二个按钮,这个按钮用于取消该任务。
      import { race, take, call } from 'redux-saga/effects'
      
      function* backgroundTask() {
        while (true) { ... }
      }
      
      function* watchStartBackgroundTask() {
        while (true) {
          yield take('START_BACKGROUND_TASK')
          yield race({
            task: call(backgroundTask),
            cancel: take('CANCEL_TASK')
          })
        }
      }
      
  • 相关阅读:
    sbt设置
    scala高级内容(二)
    scala高级内容(一) Case Class
    xubuntu手记
    ScalaTour 2.函数
    ScalaTour-1.基础
    springboot对jsp模板引擎的支持
    springboot对Thymeleaf模板引擎的支持
    SpringBoot接收参数的七种方式
    idea快捷代码提示和修改
  • 原文地址:https://www.cnblogs.com/ssaylo/p/12808218.html
Copyright © 2020-2023  润新知