ホームページ >ウェブフロントエンド >jsチュートリアル >redux-sagaの使い方、redux-sagaを使うための方法やテクニックとは?

redux-sagaの使い方、redux-sagaを使うための方法やテクニックとは?

亚连
亚连オリジナル
2018-05-31 14:13:393835ブラウズ

この記事では主に redux-saga の最初の導入と使用方法を紹介し、参考にしていきます。

redux-saga は Redux アプリケーションの非同期操作を管理するミドルウェアであり、その機能は redux-thunk + async/await に似ており、Saga を作成することですべての非同期操作ロジックを 1 か所に保存します。

redux-saga エフェクト

redux-saga エフェクトは、saga ミドルウェアによって実行されるいくつかの命令を含むプレーン テキストの JavaScript オブジェクトです。これらの命令によって実行される操作には、次の 3 つのタイプが含まれます:

  1. 非同期呼び出しの開始 (Ajax リクエストの送信など)

  2. ストアを更新する他のアクションの開始

  3. 他の Saga の呼び出し

Effects には、非同期 API リファレンスに含まれる多くの命令が含まれています。redux-saga の機能は、次のようなテストに便利です。処理のためにサガに集中しています

watch/worker (listening->execution) 作業フォーム

はジェネレータとして実装されています

複雑な非同期ロジックを含むアプリケーションシナリオを適切にサポートしています

  1. 非同期ロジックを実装よりきめ細かい方法で実行できるため、プロセスがより明確になり、バグの追跡と解決が容易になります。

  2. 人間の思考ロジックにより沿った、同期的な方法で非同期ロジックを記述します

  3. redux-thunk から redux-saga へ

  4. 今、次のようなシナリオがあるとします。ユーザーは、ログイン時のユーザーのユーザー名とパスワードが要件を満たしていること。

  5. redux-thunkを使用して実装します

  6. ユーザーデータを取得するロジック(user.js):

  7. assert.deepEqual(iterator.next().value, call(Api.fetch, '/products'))
  8. ログインを検証するロジック(login.js):

// user.js

import request from 'axios';

// define constants
// define initial state
// export default reducer

export const loadUserData = (uid) => async (dispatch) => {
  try {
    dispatch({ type: USERDATA_REQUEST });
    let { data } = await request.get(`/users/${uid}`);
    dispatch({ type: USERDATA_SUCCESS, data });
  } catch(error) {
    dispatch({ type: USERDATA_ERROR, error });
  }
}

redux-saga

すべての非同期ロジックは saga.js に書き込むことができます:

import request from 'axios';
import { loadUserData } from './user';

export const login = (user, pass) => async (dispatch) => {
  try {
    dispatch({ type: LOGIN_REQUEST });
    let { data } = await request.post('/login', { user, pass });
    await dispatch(loadUserData(data.uid));
    dispatch({ type: LOGIN_SUCCESS, data });
  } catch(error) {
    dispatch({ type: LOGIN_ERROR, error });
  }
}

redux-saga にはまだ難しい問題がたくさんありますそしてわかりにくい場所著者は、混乱しやすいと思われる概念を次のように整理しました。

take の使用 take と takeEvery はどちらも特定のアクションを監視しますが、それらの機能は一貫性がありません。 takeEvery はアクションが実行されるたびに応答します。がトリガーされ、実行フローが take ステートメントに到達した場合にのみ take が応答します。 takeEvery はアクションをリッスンし、対応する処理関数を実行するだけであり、アクションがいつ実行されるか、および呼び出されたタスクがいつ呼び出されるかを制御することはできず、いつ停止するかを制御することもできません。 listen は、アクションが一致するたびに何度でも呼び出すことができます。ただし、Take は、ジェネレーター関数のアクションと応答後の後続の操作にいつ応答するかを決定できます。 たとえば、すべてのタイプのアクション トリガーを監視するときにロガー操作を実行するには、takeEvery を使用して次を実装します:

export function* loginSaga() {
 while(true) {
  const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST
  try {
   let { data } = yield call(loginRequest, { user, pass }); //阻塞,请求后台数据
   yield fork(loadUserData, data.uid); //非阻塞执行loadUserData
   yield put({ type: LOGIN_SUCCESS, data }); //发起一个action,类似于dispatch
  } catch(error) {
   yield put({ type: LOGIN_ERROR, error });
  } 
 }
}

export function* loadUserData(uid) {
 try {
  yield put({ type: USERDATA_REQUEST });
  let { data } = yield call(userRequest, `/users/${uid}`);
  yield put({ type: USERDATA_SUCCESS, data });
 } catch(error) {
  yield put({ type: USERDATA_ERROR, error });
 }
}

takeEvery を使用して次を実装します:

import { takeEvery } from 'redux-saga'

function* watchAndLog(getState) {
 yield* takeEvery('*', function* logger(action) {
   //do some logger operation //在回调函数体内
 })
}
ここで、while(true) はプロセスの終わりに達すると、1 つのステップ (ロガー) は、新しい任意のアクションを待機することによって、新しい反復 (ロガー プロセス) を開始します。

ブロッキングと非ブロッキング

呼び出し操作は、ジェネレーターの場合、非同期操作を開始するために使用されます。ジェネレーターの呼び出しが終了するまでは、他の操作を実行したり処理したりすることはできません。ただし、fork は非ブロック操作です。fork がタスクをモバイル化すると、その時点で、実行フローは結果が返されるのを待たずに実行を継続できます。

たとえば、次のログイン シナリオ:

import { take } from 'redux-saga/effects'

function* watchAndLog(getState) {
 while(true) {
  const action = yield take('*')
  //do some logger operation //与 take 并行 
 })
}

承認リクエストの呼び出し時に結果が返されなかったが、この時点でユーザーがログアウト アクションをトリガーした場合、この時点のログアウトは無視され、実行されません。処理中。loginFlow が承認でブロックされており、take('LOGOUT') が実行されないためです

複数のタスクを同時に実行してください

特定のシナリオに遭遇し、複数のタスクを同時に実行する必要がある場合は、次のような場合があります。ユーザーデータと製品データをリクエストする場合、次のメソッドを使用する必要があります:

function* loginFlow() {
 while(true) {
  const {user, password} = yield take('LOGIN_REQUEST')
  const token = yield call(authorize, user, password)
  if(token) {
   yield call(Api.storeItem({token}))
   yield take('LOGOUT')
   yield call(Api.clearItem('token'))
  }
 }
}

yield の後に配列が続く場合、配列内の操作は Promise.all の実行ルールに従って実行されます。すべてのエフェクトが実行されるまで、ジェネレーターはブロックされます。

ソース コードの解釈

redux-saga を使用するすべてのプロジェクトでは、メイン ファイルには、saga ミドルウェアをストアに追加するための次のロジックが含まれます:

import { call } from 'redux-saga/effects'
//同步执行
const [users, products] = yield [
 call(fetch, '/users'),
 call(fetch, '/products')
]

//而不是
//顺序执行
const users = yield call(fetch, '/users'),
   products = yield call(fetch, '/products')

createSagaMiddleware が redux である場合 - saga コア ソース コード ファイル src/middleware.js にエクスポートされたメソッド:

const sagaMiddleware = createSagaMiddleware({sagaMonitor})
const store = createStore(
 reducer,
 applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)

这段逻辑主要是执行了 sagaMiddleware(),该函数里面将 runSaga 赋值给 sagaMiddleware.run 并执行,最后返回 middleware。 接着看 runSaga() 的逻辑:

export function runSaga(options, saga, ...args) {
...
 const task = proc(
  iterator,
  channel,
  wrapSagaDispatch(dispatch),
  getState,
  context,
  { sagaMonitor, logger, onError, middleware },
  effectId,
  saga.name,
 )

 if (sagaMonitor) {
  sagaMonitor.effectResolved(effectId, task)
 }

 return task
}

这个函数里定义了返回了一个 task 对象,该 task 是由 proc 产生的,移步 proc.js:

export default function proc(
 iterator,
 stdChannel,
 dispatch = noop,
 getState = noop,
 parentContext = {},
 options = {},
 parentEffectId = 0,
 name = 'anonymous',
 cont,
) {
 ...
 const task = newTask(parentEffectId, name, iterator, cont)
 const mainTask = { name, cancel: cancelMain, isRunning: true }
 const taskQueue = forkQueue(name, mainTask, end)
 
 ...
 
 next()
 
 return task

 function next(arg, isErr){
 ...
   if (!result.done) {
    digestEffect(result.value, parentEffectId, '', next)
   } 
 ...
 }
}

其中 digestEffect 就执行了 effectTriggerd() 和 runEffect(),也就是执行 effect,其中 runEffect() 中定义了不同 effect 执行相对应的函数,每一个 effect 函数都在 proc.js 实现了。

除了一些核心方法之外,redux-saga 还提供了一系列的 helper 文件,这些文件的作用是返回一个类 iterator 的对象,便于后续的遍历和执行, 在此不具体分析。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

JavaScript 隐性类型转换步骤浅析

JavaScript的数据类型转换原则

p5.js入门教程之小球动画示例代码

以上がredux-sagaの使い方、redux-sagaを使うための方法やテクニックとは?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。