Fermer

juillet 21, 2022

Gestion du middleware avec Redux-Saga

Gestion du middleware avec Redux-Saga


La gestion de l’état d’un projet depuis le frontend peut être stressante, surtout s’il n’y a pas de logique spécifiée. Redux-Saga facilite les choses avec la possibilité de tester.

Une tâche essentielle d’un développeur frontend est de gérer la façon dont les données circulent du backend vers le frontend. Cela inclut la gestion de l’état actuel, le partage des données entre les composants et la tentative de ne pas répéter le processus de récupération des mêmes données deux fois. Redux s’occupe de cette tâche sans effort.

Dans cet article, nous nous concentrerons davantage sur la gestion des états avec Redux et sur la manière d’utiliser Redux-Saga comme middleware pour faciliter la gestion des états.

Voici ce que nous couvrirons dans le post:

  • Introduction à Redux
  • Qu’est-ce qu’un middleware ?
  • Pourquoi intergiciel ?
  • Introduction à Redux-Saga
  • Comment configurer Redux-Saga
  • Comment utiliser Redux-Saga avec Redux
  • Assistant de saga et créateur d’effets
  • Utiliser Saga dans un projet React

Prérequis

Pour suivre cet article, vous devez avoir :

  • Node installé sur votre PC
  • Compréhension de base de React
  • Compréhension de base de Redux
  • Un éditeur de texte

Introduction à Redux

Redux est un magasin de données central pour toutes les données d’une application. Il aide n’importe quel composant de l’application à accéder efficacement aux données dont il a besoin, ce qui facilite grandement la gestion de l’état.

App.js a deux branches : store et header.  Sous le magasin sont la catégorie et les produits.  Sous l'en-tête se trouvent le panier et le menu.  Le magasin et le panier se connectent tous deux à un magasin de données central Redux.

L’image ci-dessus contient une représentation d’un flux d’application simple. Ce flux est basé sur les composants. Examinons un scénario dans lequel le composant magasin dispose de toutes les données pour les produits à utiliser sur l’application. Ce sera facile si nous voulons transmettre les données au composant de catégorie ou au composant de produits.

Nous pouvons le transmettre en tant qu’accessoires, mais cela devient plus difficile à réaliser lorsque nous essayons de transmettre les données au composant cart. Le chemin emprunté par la plupart des développeurs pour résoudre le problème consiste à déplacer les données vers le composant de l’application ; ensuite, les données seront transmises en tant qu’accessoires aux composants.

Cela aide, mais cela devient encore plus frustrant lorsqu’il s’agit d’un gros projet où vous avez beaucoup de composants qui passent des accessoires. Cette approche peut ne pas être aussi efficace, en particulier lorsque vous regardez du point de vue de l’optimisation – toute modification apportée à un composant déclenchera une actualisation de tous les composants avec des accessoires associés. Cela affecte le temps de chargement des utilisateurs.

Le moyen de résoudre efficacement ce problème consiste à utiliser un support de gestion d’état – Redux intervient ici. Comme défini précédemment, Redux est un magasin central où les données sont stockées pour être accessibles par n’importe quel composant de l’application.

Qu’est-ce qu’un intergiciel ?

Le middleware dans Redux est un moyen d’étendre les fonctionnalités personnalisées ; cela donne des fonctionnalités supplémentaires au Redux existant. Il fournit une extension tierce avec des points entre l’envoi de l’action et le moment où elle atteint le réducteur. Le middleware peut également être utilisé pour les rapports de plantage, la journalisation, l’exécution asynchrone d’une tâche, etc.

Pourquoi le middleware ?

Nous utilisons des amplificateurs pour remplacer la fonction de répartition pour Redux, mais nous sommes parfois intéressés par la personnalisation de la fonction de répartition. Redux utilise un middleware pour personnaliser les fonctions de répartition. Certaines autres bibliothèques comme Express utilisent également des intergiciels pour personnaliser un comportement spécifique dans une application.

Introduction à Redux-Saga

Redux-Saga est une bibliothèque compagnon pour Redux qui gère efficacement le flux asynchrone d’une application. Il permet au magasin Redux de communiquer de manière asynchrone avec des ressources extérieures au magasin, ce qui inclut l’accès au stockage local, les requêtes HTTP et l’exécution de services d’entrée et de sortie gérés efficacement.

Redux-Saga est un exemple de middleware Redux ; d’autres types incluent Redux Thunketc.

Commencer

Nous allons créer une application de base capable de récupérer une liste d’utilisateurs à partir d’une API, et nous gérerons l’état à l’aide de Redux et Redux-Saga. Entrez la commande ci-dessous dans un terminal pour créer un projet React.

npx create-react-app users

Cette commande créera un modèle vide create-react-app. Ouvrez le fichier de projet sur votre éditeur de texte préféré.

Installons toutes les dépendances nécessaires : react-redux, redux, redux-saga et bootstrap. Utilisez la commande ci-dessous pour les installer.

yarn add react-redux redux redux-saga boostrap

Ouvrez le dossier racine et créez un dossier appelé redux. À l’intérieur, créez deux sous-dossiers nommés actions et reducers. Enfin, créez un fichier nommé store.js et ajoutez le code suivant dans le fichier.

import { createStore } from "redux";
import rootReducer from "./reducers";

const store = createStore(rootReducer);
export default store;

Dans le code ci-dessus, nous importons createStore depuis Redux pour créer un magasin Redux, et nous importons le rootReducer, qui contient tous les réducteurs que nous aurons dans le projet.

Ensuite, nous avons créé une variable et lui avons attribué le magasin que nous allons créer. Maintenant, créons nos réducteurs. Tout d’abord, à l’intérieur des réducteurs de dossiers, créez un fichier index.js et un fichier users.js ; le réducteur que nous utiliserons dans le projet sera lié au fichier index.js, tandis que le fichier users.js contiendra le réducteur utilisateur. Collez le code suivant dans le fichier index.js :

import { combineReducers } from "redux";

const rootReducer = combineReducers({
 
});
export default rootReducer

Nous utilisons les combineReducers pour combiner tous les réducteurs en un seul endroit, qui est le rootReducer. Nous ajouterons les réducteurs à l’intérieur plus tard.

Travaillons maintenant sur notre réducteur d’utilisateurs. Ajoutez le code suivant dans le fichier user.js :

import * as types from '../types';

const initialState = {
 users: []
}

export default function users(state=initialState, action) {
 switch (action.type) {
  case type.GET_USERS;
   return {
    ...state,
    users: action.payload;
   }
  default: 
   return state;
 }
}

Dans le code ci-dessus, nous importons des types que nous créerons plus tard, puis nous définissons initialState sur l’état par défaut du magasin ; c’est ce que nous transmettrons au réducteur des utilisateurs. Chaque réducteur dans Redux prend deux paramètres : l’état initial et l’action. Le réducteur utilise un commutateur pour vérifier le type d’action qui sera utilisé pour déterminer la valeur de retour.

Nous allons maintenant ajouter le réducteur au rootReducer que nous avons créé précédemment. Utilisons ce code ci-dessous pour mettre à jour le fichier reducers index.js :

import { combineReducers } from "redux";
import Users from "./users";
const rootReducer = combineReducers({
  users: Users,
})
export default rootReducer;

Créons nos types, créons un fichier types.js dans le dossier redux et ajoutons le code suivant dans le fichier :

export const GET_USERS = "GET_USERS";

Maintenant, créons des actions pour nos réducteurs. Créez un fichier users.js dans le dossier actions et ajoutez le code suivant dans le fichier.

import * as types from "../types"

export function getUsers(users) {
 return {
  type: type.GET_USERS(),
  payload: users,
 }
}

Enfin, ajoutons le fournisseur au fichier index.js dans le dossier racine. Mettez à jour le fichier index.js avec le code ci-dessous :

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import store from './redux/store';
import 'bootstrap/dist/css/bootstrap.min.css';
ReactDOM.render(
 <Provider store={store}>
  <React.StrictMode>
   <App />
  </React.StrictMode>
 </Provider>,
 document.getElementById('root')
);

Nous ajoutons le fournisseur en tant que wrapper pour couvrir l’ensemble du projet ; cela permet aux données d’être partagées à travers notre projet. Le fournisseur accepte le magasin que nous avons créé contenant les données que nous stockons.

À l’intérieur du composant de la carte, ajoutons le code suivant.

import React from 'react'
const Card = ({user}) => {
  return (
    <div className="card">
      <div className="card-body">
        <div className="card-title">{user.name}</div>
        <div className="card-subtitle mb-2 text-muted">{user.company.name}</div>
        <div className="card-text">{user.company.catchPhrase}</div>
      </div>
    </div>
  )
}
export default Card

À l’intérieur du composant, nous obtenons les données de l’utilisateur sous forme d’accessoires et les affichons en fonction du nom de l’utilisateur, de l’entreprise et de la catchPhrase de l’entreprise. Ensuite, ajoutez le code suivant au composant Users.

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getUser } from '../redux/actions/users'
import Card from "./Card"
const Users = () => {
  const dispatch = useDispatch()
  const users = useSelector(state => state.users.users)

  useEffect(() => {
    dispatch(getUser([{
     id: 1, 
     name: "Emmanuel",
     company: "Dusk",
     catchPhrase: "Made to fly"
    }]));
  }, [dispatch])
  return (
    <>
      {
        users.length > 0 && users.map(user => (
          <Card user={user} key={user.id} /> 
        ))
      }
      { users.length === 0 ? <p>No users</p> : null }
    </>
  )
}
export default Users

Dans le code ci-dessus, nous importons useDispatch et useSelector. Le useDispatch renvoie une référence d’expédition du magasin que nous avons créé, tandis que le useSelector nous permet d’extraire des données du magasin.

Nous utilisons le useSelector pour obtenir les données des utilisateurs du magasin. En revanche, nous utilisons la méthode useEffect pour définir temporairement les données des utilisateurs à l’aide de la fonction dispatch, en attendant le moment où nous ajouterons le middleware. Nous parcourons les données des utilisateurs pour que les données de chaque utilisateur soient transmises au composant de la carte.

Mettons à jour le fichier app.css avec ce style pour lui donner l’effet que nous voulons.

.App {
 margin: 5%;
}
.card {
 margin: 10px;
}

Maintenant, ajoutons redux dev afin que nous puissions gérer l’état à travers lui. Ouvrez d’abord le store.js et mettez-le à jour avec le code ci-dessous.

import { createStore, compose } from 'redux';
import rootReducer from './reducers/index';
const store = compose(
  applyMiddleware(sagaMiddleware),
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )(createStore)(rootReducer);
export default store;

Maintenant, configurons notre middleware. Créez un sous-dossier dans le dossier src nommé saga et ajoutez les fichiers index.js et userSaga.js dans le dossier.

Commençons par le fichier userSaga.js : ajoutez le code suivant dans le fichier :

import { call, put, takeEvery } from 'redux-saga/effects';
const apiUrl = 'https://jsonplaceholder.typicode.com/users';
function getApiData() {
 return fetch(apiUrl).then(response => response.json().catch(error => error));
}

function* fetchUsers(action) {
 try {
  const users = yield call(getApiData);
  yield put({ type: 'GET_USERS_SUCCESS', users: users });
 } catch (error) {
  yield put({ type: 'GET_USERS_FAILED', message: error.message });
 }
}

function* userSaga() {
 yield takeEvery('GET_USERS_REQUESTED', fetchUsers);
}

export default userSaga;

Le déclenchement d’un effet secondaire de Redux-Saga se fait par le processus de production d’effets déclaratifs. Redux-Saga composera toujours ces effets ensemble pour faire fonctionner un flux de contrôle. L’utilisation d’effets tels que call et put avec takeEvery atteint le même objectif que Redux Thunk, c’est-à-dire qu’il sert de middleware avec la testabilité comme avantage supplémentaire.

Dans le code ci-dessus, nous importons put, call et takeEvery de Redux-Saga. Nous les utiliserons pour obtenir notre fonctionnalité middleware. Nous avons donc créé une variable apiUrl pour stocker le lien URL de l’API, et nous avons également créé une fonction getApiData qui récupère les données utilisateur à partir du point de terminaison de l’API.

Ensuite, nous commençons à créer un générateur pour la saga. Le générateur fetchUsers obtient un paramètre d’actions et utilise la méthode try-catch. La méthode try utilise l’effet d’appel pour produire le getApiData. Ensuite, en utilisant l’effet put, il définit le type et l’action de la fonction de répartition en fonction de la fonction de répartition.

Ensuite, nous créons le générateur userSaga qui prend le générateur fetchUsers et utilise l’effet takeEvery pour le céder au type GET_USER_REQUESTED.

Enfin, ajoutons ce code au fichier index.js dans le sous-dossier saga.

import { all } from "redux-saga/effects";
import userSaga from "./userSaga";
export default function* rootSaga() {
 yield all([userSaga()]);
}

Dans le code ci-dessus, nous importons tout de redux-saga/effects et importons userSaga à partir du fichier userSaga que nous avons créé précédemment. Nous avons créé un générateur qui cède le userSaga au magasin en utilisant l’effet all.

Nous devrons apporter quelques modifications à notre code précédent. Ouvrez le store.js et mettez-le à jour avec le code ci-dessous.

import { createStore, compose, applyMiddleware } from 'redux';
import rootReducer from './reducers/index';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './saga/index';
const sagaMiddleware = createSagaMiddleware();
const store = compose(
  applyMiddleware(sagaMiddleware),
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )(createStore)(rootReducer);
  sagaMiddleware.run(rootSaga);
export default store;

Les modifications ci-dessus définissent la Redux-Saga que nous avons créée en tant que middleware. Ensuite, ouvrez votre fichier types.js et mettez-le à jour avec le code ci-dessous.

export const GET_USERS_REQUESTED = 'GET_USERS_REQUESTED';
export const GET_USERS_SUCCESS = 'GET_USERS_SUCCESS';
export const GET_USERS_FAILED = 'GET_USERS_FAILED';

Maintenant, ouvrez le dossier reducers et mettez à jour le fichier users.js avec le code suivant.

import * as type from "../types";
const initalState = {
  users: [],
  loading: false,
  error: null
}
export default function users(state = initalState, action) {
  switch (action.type) {
    case type.GET_USERS_REQUESTED:
      return {
        ...state,
        loading: true
      }
    case type.GET_USERS_SUCCESS:
      return {
        ...state,
        loading: false,
        users: action.users
      }
    case type.GET_USERS_FAILED:
      return {
        ...state,
        loading: false,
        error: action.message
      }
    default:
      return state;
  }
}

Dans le code ci-dessus, nous avons mis à jour l’état initial et y avons ajouté les actions que nous avons créées et le middleware. Accédez au composant User et mettez-le à jour avec le code suivant.

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getUser } from '../redux/actions/users'
import Card from "./Card"
const Users = () => {
  const dispatch = useDispatch()
  const users = useSelector(state => state.users.users)
  const loading = useSelector(state => state.users.loading)
  const error = useSelector(state => state.users.error)
  useEffect(() => {
    dispatch(getUser());
  }, [dispatch])
  return (
    <>
      {
        users.length > 0 && users.map(user => (
          <Card user={user} key={user.id} /> 
        ))
      }
      { users.length === 0 ? <p>No users</p> : null }
      { users.length === 0 && loading === true ? <p>Loading...</p> : null }
      { error === 0 && !loading === true ? <p>{error.message}</p> : null }
    </>
  )
}
export default Users

Enfin, ajoutez cette mise à jour au fichier users.js dans le dossier actions.

import * as types from "../types";
export function getUser(users) {
  return {
    type: types.GET_USERS_REQUESTED,
    payload: users,
  }
}

Maintenant, tout est parfaitement fait. Ouvrez votre terminal et exécutez le projet à l’aide de la commande suivante.

yarn start
//or

npm start

Dans votre navigateur, vous devriez voir une page avec un contenu similaire à celui présenté dans l’image ci-dessous.

une liste de cartes avec le nom de la personne, l'entreprise et le slogan

Conclusion

Dans cet article, nous avons découvert Redux, le middleware, pourquoi et où utiliser le middleware et Redux-Saga. Nous avons démontré tout cela à l’aide d’un projet simple ; vous pouvez facilement reproduire cela pour les grands projets afin de gérer facilement l’état.

Ensuite, vous voudrez peut-être en savoir plus sur Recul.




Source link