web-dev-qa-db-fra.com

Quelle est la façon idiomatique de démarrer rootSaga?

le projet redux-saga existe depuis assez longtemps maintenant, mais il y a encore beaucoup de choses confuses à propos de cette bibliothèque. Et l'un d'eux est: comment démarrer votre rootSaga. Par exemple, dans le tutoriel pour débutant rootSaga est démarré en créant un tableau de sagas. Comme ça

export default function* rootSaga() {
  yield [
    helloSaga(),
    watchIncrementAsync()
  ]
}

Cependant, dans la section à l'aide des assistants de saga rootSaga se compose de deux sagas fourchues. Comme ça:

export default function* rootSaga() {
  yield fork(watchFetchUsers)
  yield fork(watchCreateUser)
}

La même façon de démarrer rootSaga est utilisée dans l'exemple asynchrone dans le dépôt redux-saga. Cependant, si vous consultez des exemples du monde réel et des cartes d'achat, vous verrez que rootSagas yeild un tableau de sagas fourchues. Comme ça:

export default function* root() {
  yield [
    fork(getAllProducts),
    fork(watchGetProducts),
    fork(watchCheckout)
  ]
}

De plus, si vous lisez certaines discussions sur les problèmes de redux-saga, vous verrez que certaines personnes suggèrent d'utiliser spawn au lieu de fork pour rootSaga pour empêcher votre application de se bloquer complètement si l'une de vos sagas forkées est annulée en raison d'une exception non gérée.

Alors, quelle est la meilleure façon de démarrer votre rootSaga? Et quelles sont les différences entre celles existantes?

35
slava shkodin

Vous pouvez démarrer plusieurs sagas racine. Mais toute saga a la possibilité de démarrer une autre saga par elle-même. Ainsi, il est possible de démarrer une seule saga racine, qui crée les autres sagas.

Vous avez juste besoin de savoir comment les erreurs se propagent à la saga parent. Si vous avez une seule saga racine et qu'une saga enfant s'est écrasée, par défaut l'erreur se propage au parent qui se terminera, ce qui tuera également toutes les autres sagas démarrées à partir de ce parent.

C'est à vous de décider de ce comportement. Selon votre application, vous souhaiterez peut-être avoir un comportement rapide (rendre toute l'application inutilisable s'il y a un tel problème), ou échouer en toute sécurité, et essayer de faire fonctionner l'application même si certaines parties peuvent avoir des problèmes.

En règle générale, je vous recommande de démarrer plusieurs sagas racine, ou votre saga parent utilise spawn au lieu de fork afin que votre application reste utilisable en cas de panne. Notez qu'il est également assez facile d'oublier de détecter des erreurs à certains endroits. Vous ne voulez généralement pas, par exemple, que toute votre application devienne inutilisable s'il y a une seule demande d'API qui échoue

Edit : Je recommanderais de jeter un œil à https://github.com/yelouafi/redux-saga/issues/57

Dans ce problème redux-saga , je montre différentes façons de démarrer des sagas et l'impact que cela a sur votre application.

[~ # ~] tldr [~ # ~] : c'est ainsi que je démarre habituellement les sagas root:

const makeRestartable = (saga) => {
  return function* () {
    yield spawn(function* () {
      while (true) {
        try {
          yield call(saga);
          console.error("unexpected root saga termination. The root sagas are supposed to be sagas that live during the whole app lifetime!",saga);
        } catch (e) {
          console.error("Saga error, the saga will be restarted",e);
        }
        yield delay(1000); // Workaround to avoid infinite error loops
      }
    })
  };
};

const rootSagas = [
  domain1saga,
  domain2saga,
  domain3saga,
].map(makeRestartable);

export default function* root() {
  yield rootSagas.map(saga => call(saga));
}
14
Sebastien Lorber

Comment créer rootSaga?

Selon un développeur principal de redux-saga [1 , 2] la façon idiomatique de créer rootSaga est d'utiliser all Effect Combinator . Veuillez également noter que la production de tableaux à partir de sagas est déconseillée .

Exemple 1

Vous pouvez utiliser quelque chose comme this (+ all)

import { fork, all } from 'redux-saga/effects';
import firstSaga from './firstSaga';
import secondSaga from './secondSaga';
import thirdSaga from './thirdSaga';

export default function* rootSaga() {
    yield all([
        fork(firstSaga),
        fork(secondSaga),
        fork(thirdSaga),
    ]);
}

Exemple 2

Tiré d'ici

// foo.js
import { takeEvery } from 'redux-saga/effects';
export const fooSagas = [
  takeEvery("FOO_A", fooASaga),
  takeEvery("FOO_B", fooBSaga),
]

// bar.js
import { takeEvery } from 'redux-saga/effects';
export const barSagas = [
  takeEvery("BAR_A", barASaga),
  takeEvery("BAR_B", barBSaga),
];

// index.js
import { fooSagas } from './foo';
import { barSagas } from './bar';

export default function* rootSaga() {
  yield all([
    ...fooSagas,
    ...barSagas
  ])
}

fourchette vs frai

fork et spawn renverra tous deux des objets Task . Les tâches fourchues sont attachées au parent, tandis que les tâches générées sont détachées du parent.

  • Gestion des erreurs dans les fourches [ link ]:

    Erreurs des tâches enfants remontent automatiquement à leurs parents. Si une tâche bifurquée génère une erreur non interceptée, alors la tâche la tâche parent le sera abandonnera avec l'erreur enfant, et l'arbre l'arbre d'exécution du parent entier (c'est-à-dire les tâches fourchues + la tâche principale représentée par le corps du parent si elle est toujours en cours d'exécution) sera annulée.

  • Gestion des erreurs dans les tâches générées [ link ]:

    Le parent n'attendra pas la fin des tâches détachées avant de retourner et tous les événements qui peuvent affecter le parent ou la tâche détachée sont complètement indépendants ( erreur, annulation).

Sur la base de ce qui précède, vous pouvez utiliser fork pour les tâches "essentielles à la mission", c'est-à-dire "si cette tâche échoue, veuillez planter l'application entière", et réapparaître pour les tâches "non critiques", ie "si cette tâche échoue, ne propagez pas l'erreur au parent".

12
np8