Redux Saga est un middleware Redux qui simplifie la gestion des effets secondaires dans les applications Redux. Grâce à l’utilisation des générateurs (function*
) d’ES6, il permet d’écrire du code asynchrone qui ressemble à du code synchrone.
Redux Saga n’existe pas seulement dans le monde JavaScript, mais est également considéré comme un design pattern. Le Saga pattern est une façon de gérer les transactions longues avec de nombreux effets secondaires ou risques potentiels. Pour chaque transaction réussie, il doit y avoir une contre-transaction pour annuler la transaction et revenir à l’état initial en cas de problème.
Diagramme illustrant le concept de Saga Pattern dans la gestion des transactions
Un effet secondaire est une tâche dans la programmation fonctionnelle qui nécessite une interaction avec le monde extérieur, comme appeler une API pour récupérer des données, enregistrer des données dans le localStorage ou lire des cookies du navigateur. Ces tâches sont souvent asynchrones et peuvent modifier l’état de l’application. Les reducers dans Redux doivent être synchrones et purs, c’est-à-dire qu’ils ne doivent traiter que la logique basée sur l’entrée et renvoyer la sortie sans provoquer d’effets secondaires. Par conséquent, Redux Saga est utilisé pour gérer les effets secondaires en dehors des reducers, ce qui permet de les garder simples et faciles à tester.
Une fonction génératrice est un type de fonction spécial en JavaScript qui peut suspendre son exécution et renvoyer une valeur plusieurs fois. Le mot-clé yield
est utilisé pour suspendre l’exécution et renvoyer une valeur. Contrairement à une fonction ordinaire qui s’exécute et renvoie un résultat une seule fois, une fonction génératrice peut s’exécuter, suspendre son exécution pour renvoyer un résultat, puis reprendre son exécution à partir du point où elle s’est arrêtée.
Redux Saga fonctionne en écoutant les actions dispatchées. Lorsqu’une action est dispatchée, Redux Saga vérifie si cette action l’intéresse. Si c’est le cas, il exécute une fonction génératrice correspondante. Cette fonction génératrice va yield
des objets effets. Ces objets effets contiennent des instructions pour le middleware Redux afin d’exécuter d’autres opérations, comme appeler une fonction asynchrone ou dispatcher une nouvelle action vers le store.
Alors pourquoi utiliser Redux Saga? Comparé à Redux Thunk, un autre middleware couramment utilisé pour gérer les effets secondaires, Redux Saga offre de nombreux avantages. Redux Thunk conduit souvent à un « callback hell » lors du traitement de plusieurs tâches asynchrones imbriquées, rendant le code difficile à lire et à maintenir. Redux Saga, grâce à l’utilisation des fonctions génératrices et des objets effets, rend le code plus lisible, plus compréhensible et plus facile à tester. Redux Saga permet de tester facilement les flux asynchrones et de maintenir les actions pures.
Exemple de comparaison entre Redux Thunk et Redux Saga pour le traitement d’une requête API:
Redux Thunk:
import { API_BUTTON_CLICK, API_BUTTON_CLICK_SUCCESS, API_BUTTON_CLICK_ERROR } from './actions/consts';
import { getDataFromAPI } from './api';
const getDataStarted = () => ({ type: API_BUTTON_CLICK });
const getDataSuccess = data => ({ type: API_BUTTON_CLICK_SUCCESS, payload: data });
const getDataError = message => ({ type: API_BUTTON_CLICK_ERROR, payload: message });
const getDataFromAPI = () => {
return dispatch => {
dispatch(getDataStarted());
getDataFromAPI()
.then(data => {
dispatch(getUserSuccess(data));
})
.catch(err => {
dispatch(getDataError(err.message));
});
};
};
Redux Saga:
import { call, put, takeEvery } from 'redux-saga/effects';
import { API_BUTTON_CLICK, API_BUTTON_CLICK_SUCCESS, API_BUTTON_CLICK_ERROR } from './actions/consts';
import { getDataFromAPI } from './api';
export function* apiSideEffect(action) {
try {
const data = yield call(getDataFromAPI);
yield put({ type: API_BUTTON_CLICK_SUCCESS, payload: data });
} catch (e) {
yield put({ type: API_BUTTON_CLICK_ERROR, payload: e.message });
}
}
export function* apiSaga() {
yield takeEvery(API_BUTTON_CLICK, apiSideEffect);
}
Redux Saga permet de séparer la logique de gestion des effets secondaires des composants et des reducers, ce qui facilite la maintenance, l’extension et les tests du code. L’utilisation de try/catch
dans Redux Saga simplifie la gestion des erreurs par rapport à l’utilisation de chaînes de promesses dans Redux Thunk.