Maison  >  Article  >  interface Web  >  Comment utiliser redux-saga, et quelles sont les méthodes et techniques d'utilisation de redux-saga ?

Comment utiliser redux-saga, et quelles sont les méthodes et techniques d'utilisation de redux-saga ?

亚连
亚连original
2018-05-31 14:13:393820parcourir

Cet article présente principalement la première introduction et utilisation de redux-saga. Maintenant, je le partage avec vous et vous donne une référence.

redux-saga est un middleware qui gère les opérations asynchrones des applications Redux. Sa fonction est similaire à redux-thunk + async/await. Il stocke toute la logique des opérations asynchrones en un seul endroit pour un traitement centralisé en créant des Sagas.

Les effets de redux-saga

Les effets de redux-saga sont un objet JavaScript en texte brut qui contient certaines fonctions qui seront exécutées par l'instruction middleware de la saga. Les opérations effectuées par ces instructions comprennent les trois types suivants :

  1. Initier un appel asynchrone (comme l'envoi d'une requête Ajax)

  2. Initier d'autres action pour mettre à jour le Store

  3. Appeler d'autres Sagas

Effects contient de nombreuses instructions, qui peuvent être trouvées dans la référence de l'API asynchrone

Caractéristiques de redux-saga

Commodité pour les tests, par exemple :

assert.deepEqual(iterator.next().value, call(Api.fetch, '/products'))

  1. l'action peut conserver sa pureté, et les opérations asynchrones sont concentrées dans la saga pour le traitement

  2. travail de surveillance/travailleur (surveillance->exécution) Le formulaire

  3. est implémenté en tant que générateur

  4. , qui prend en charge les scénarios d'application contenant une logique asynchrone complexe

  5. Implémentez une logique asynchrone de manière plus fine, rendant le processus plus clair et plus facile à suivre et à résoudre les bogues.

  6. Écrire une logique asynchrone de manière synchrone, ce qui est plus conforme à la logique de la pensée humaine

  7. De redux-thunk à redux-saga

Supposons qu'il y ait maintenant un scénario : lorsque l'utilisateur se connecte, il doit vérifier si le nom d'utilisateur et le mot de passe de l'utilisateur répondent aux exigences.

Utilisez redux-thunk pour implémenter

La logique d'obtention des données utilisateur (user.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 });
  }
}

Logique de vérification de connexion (login.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

Toute la logique asynchrone peut être écrite dans saga.js :

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 });
 }
}

Interprétation difficile

Pour redux-saga, il y a encore beaucoup de choses difficiles à comprendre et obscures. L'auteur ci-dessous organise les concepts que je trouve plus déroutants :

<.>

Utilisation de take

take et takeEvery surveillent une action, mais leurs fonctions sont incohérentes. takeEvery répond à chaque fois que l'action est déclenchée, tandis que take est le flux d'exécution. répondre lorsque l'instruction take est exécutée. takeEvery écoute uniquement l'action et exécute la fonction de traitement correspondante. Il n'a pas beaucoup de contrôle sur le moment où l'action est exécutée et sur la manière de répondre à l'action. Les tâches appelées ne peuvent pas contrôler le moment où elles sont appelées et elles ne peuvent pas contrôler le moment où elles doivent s'arrêter. l'écoute. Il ne peut être appelé qu'à chaque fois qu'une action correspond. Mais take peut décider quand répondre à une action et aux opérations ultérieures après la réponse dans la fonction génératrice.

Par exemple, pour effectuer une opération d'enregistrement lors de la surveillance de tous les types de déclencheurs d'action, utilisez takeEvery pour implémenter ce qui suit :

import { takeEvery } from &#39;redux-saga&#39;

function* watchAndLog(getState) {
 yield* takeEvery(&#39;*&#39;, function* logger(action) {
   //do some logger operation //在回调函数体内
 })
}

Utilisez take pour implémenter ce qui suit :

import { take } from &#39;redux-saga/effects&#39;

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

où (vrai) signifie qu'une fois la dernière étape du processus (logger) atteinte, démarrer une nouvelle itération en attendant une nouvelle action arbitraire (processus enregistreur).

Blocage et non-blocage

L'opération d'appel est utilisée pour lancer des opérations asynchrones, l'appel est une opération bloquante et ne peut être exécuté que lorsque l'appel du générateur est terminé. terminé. Effectuer ou traiter toute autre affaire. , mais fork est une opération non bloquante. Lorsque fork mobilise une tâche, la tâche sera exécutée en arrière-plan. À ce moment, le flux d'exécution peut continuer à s'exécuter sans attendre le retour du résultat.

Par exemple, le scénario de connexion suivant :

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

Si le résultat n'est pas renvoyé lors de l'appel pour demander une autorisation, mais à ce moment-là fois que l'utilisateur L'action LOGOUT est à nouveau déclenchée. À ce moment, LOGOUT sera ignoré et non traité car loginFlow est bloqué lors de l'autorisation et n'est pas exécuté pour prendre ('LOGOUT') . en même temps Plusieurs tâches

Si vous rencontrez un scénario dans lequel vous devez effectuer plusieurs tâches en même temps, comme demander des données sur les utilisateurs et les données sur les produits, vous devez utiliser la méthode suivante :

Lorsque le rendement est suivi d'un tableau, les opérations dans le tableau seront exécutées selon les règles d'exécution de Promise.all, et le générateur bloquera jusqu'à ce que tous les effets sont exécutés
import { call } from &#39;redux-saga/effects&#39;
//同步执行
const [users, products] = yield [
 call(fetch, &#39;/users&#39;),
 call(fetch, &#39;/products&#39;)
]

//而不是
//顺序执行
const users = yield call(fetch, &#39;/users&#39;),
   products = yield call(fetch, &#39;/products&#39;)

Interprétation du code source

Dans chaque projet utilisant redux-saga, le fichier principal aura la logique suivante pour ajouter des sagas middleware vers le Store :

où createSagaMiddleware est la méthode exportée dans le fichier de code source principal de 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 = &#39;anonymous&#39;,
 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, &#39;&#39;, next)
   } 
 ...
 }
}

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

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

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

相关文章:

JavaScript 隐性类型转换步骤浅析

JavaScript的数据类型转换原则

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn