One place for hosting & domains

      Comprendre les actions asynchrones de Redux avec Redux Thunk


      Introduction

      Par défaut, les actions de Redux sont expédiées de façon synchrone, ce qui pose un problème pour toute application non triviale qui doit communiquer avec une application externe ou effectuer des effets secondaires. Redux permet également au middleware qui se trouve au milieu d’une action d’être distribué et à l’action d’atteindre les réducteurs.

      Il existe deux bibliothèques de middleware très populaires qui prennent en charge les effets spéciaux et les actions asynchrones : Redux Thunk et Redux Saga. Cette publication vous emmène à la découverte de Redux Thunk.

      Thunk est un concept de programmation dans lequel une fonction est utilisée pour retarder l’évaluation/le calcul d’une opération.

      Redux Thunk est un middleware qui vous permet de faire un appel à l’action auprès des créateurs qui renvoie une fonction au lieu d’un objet d’action. Cette fonction reçoit la méthode de distribution du store. Elle permet donc d’envoyer des actions synchrones régulières dans le corps de la fonction une fois que les opérations asynchrones ont été terminées.

      Dans cet article vous allez apprendre à ajouter Redux Thunk et découvrir de quelle manière il peut s’adapter à une application hypothétique de Todo.

      Conditions préalables

      Cet article suppose que vous disposez de certaines connaissances de base sur React et Redux. Vous pouvez consulter cet article si vous débutez avec Redux.

      Ce tutoriel est construit autour d’une hypothétique application de Todo qui est supposée assurer le suivi des tâches à faire et des tâches accomplies. Nous présumons que create-react-app a été utilisée pour générer une nouvelle application React. Cependant, redux, react-redux et axios ont déjà été installés.

      Cet article ne vous donne pas d’informations plus détaillées sur la manière de créer une application de Todo de zéro. Nous la présentons ici comme un paramètre conceptuel pour mettre en avant Redux Thunk.

      Ajout de redux-thunk

      Tout d’abord, utilisez le terminal pour naviguer vers le répertoire de projets et installez le paquet redux-thunk dans votre projet :

      • npm install redux-thunk@2.3.0

      Remarque : Redux Thunk ne dispose que de 14 lignes de code. Consultez la source ici pour en savoir plus sur le fonctionnement en arrière-plan d’un middleware Redux.

      Maintenant, utilisez le middleware pour créer le store de votre application en utilisant applyMiddleware de Redux. Avec une application React intégrant redux et react-redux, votre fichier index.js devrait ressembler à ce qui suit :

      src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import { Provider } from 'react-redux';
      import { createStore, applyMiddleware } from 'redux';
      import thunk from 'redux-thunk';
      import './index.css';
      import rootReducer from './reducers';
      import App from './App';
      import * as serviceWorker from './serviceWorker';
      
      // use applyMiddleware to add the thunk middleware to the store
      const store = createStore(rootReducer, applyMiddleware(thunk));
      
      ReactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById('root')
      );
      

      Maintenant, Redux Thunk a été importé et appliqué dans votre application.

      Utilisation de Redux Thunk dans un exemple d’application

      Redux Thunk est le plus couramment utilisé pour communiquer de manière asynchrone avec une application externe afin de récupérer ou de sauvegarder des données. Redux Thunk vous permet de facilement distribuer des actions qui suivent le cycle de vie d’une requête à une application externe.

      En règle générale, pour créer un nouvel élément de todo, il faut lancer une action pour indiquer qu’une création d’un élément todo a commencé. Ensuite, si l’élément todo est bien créé et renvoyé par le serveur externe, une autre action est distribuée avec le nouvel élément todo. Si une erreur survient et le serveur ne sauvegarde pas le todo, une action accompagnée d’une erreur risque d’être déclenchée.

      Voyons comment nous pourrions le faire avec Redux Thunk.

      Importez et distribuez l’action dans votre composant de conteneur :

      src/containers/AddTodo.js

      import { connect } from 'react-redux';
      import { addTodo } from '../actions';
      import NewTodo from '../components/NewTodo';
      
      const mapDispatchToProps = dispatch => {
        return {
          onAddTodo: todo => {
            dispatch(addTodo(todo));
          }
        };
      };
      
      export default connect(
        null,
        mapDispatchToProps
      )(NewTodo);
      

      L’action utilisera Axios pour envoyer une requête POST au terminal, au niveau de JSONPlaceholder (https://jsonplaceholder.typicode.com/todos) :

      src/actions/index.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from './types';
      
      import axios from 'axios';
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(`https://jsonplaceholder.typicode.com/todos`, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      const addTodoSuccess = todo => ({
        type: ADD_TODO_SUCCESS,
        payload: {
          ...todo
        }
      });
      
      const addTodoStarted = () => ({
        type: ADD_TODO_STARTED
      });
      
      const addTodoFailure = error => ({
        type: ADD_TODO_FAILURE,
        payload: {
          error
        }
      });
      

      Notez que le créateur d’action addTodo renvoie une fonction au lieu de l’objet d’action habituel. Cette fonction reçoit la méthode d’envoi du store.

      À l’intérieur du corps de la fonction, envoyez tout d’abord une action synchrone immédiate au store pour indiquer que vous avez commencé à enregistrer le todo avec l’application externe. Ensuite, vous devez envoyer la requête POST en elle-même au serveur, en utilisant Axios. Si le serveur vous envoie une réponse positive, vous distribuez une action synchrone probante avec les données reçues via la réponse. Mais, si la réponse est un échec, nous distribuons une action synchrone différente avec le message d’erreur.

      Lorsque vous utilisez une API externe, comme JSONPlaceholder dans le cas présent, il est possible qu’il y ait un retard au niveau du réseau. Cependant, si vous travaillez avec un serveur de backend local, il se peut que les réponses du réseau soient trop rapides pour pouvoir constater le retard de réseau qu’un utilisateur réel devrait subir. Vous pouvez donc ajouter un retard artificiel au cours du développement :

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              setTimeout(() => {
                dispatch(addTodoSuccess(res.data));
              }, 2500);
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      Pour tester les scénarios d’erreur, vous pouvez lancer une erreur manuellement :

      src/actions/index.js

      // ...
      
      export const addTodo = ({ title, userId }) => {
        return dispatch => {
          dispatch(addTodoStarted());
      
          axios
            .post(ENDPOINT, {
              title,
              userId,
              completed: false
            })
            .then(res => {
              throw new Error('addToDo error!');
              // dispatch(addTodoSuccess(res.data));
            })
            .catch(err => {
              dispatch(addTodoFailure(err.message));
            });
        };
      };
      
      // ...
      

      Pour compléter, voici un exemple de ce à quoi le réducteur de todo pourrait ressembler pour prendre en charge l’intégralité du cycle de vie de la requête :

      src/reducers/todosReducer.js

      import {
        ADD_TODO_SUCCESS,
        ADD_TODO_FAILURE,
        ADD_TODO_STARTED,
        DELETE_TODO
      } from '../actions/types';
      
      const initialState = {
        loading: false,
        todos: [],
        error: null
      };
      
      export default function todosReducer(state = initialState, action) {
        switch (action.type) {
          case ADD_TODO_STARTED:
            return {
              ...state,
              loading: true
            };
          case ADD_TODO_SUCCESS:
            return {
              ...state,
              loading: false,
              error: null,
              todos: [...state.todos, action.payload]
            };
          case ADD_TODO_FAILURE:
            return {
              ...state,
              loading: false,
              error: action.payload.error
            };
          default:
            return state;
        }
      }
      

      Découverte de getState

      Non seulement la méthode de distribution de l’état est reçue, mais la fonction renvoyée par un créateur d’action asynchrone avec Redux Thunk reçoit également la méthode getState du store pour pouvoir lire les valeurs réelles du store :

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          dispatch(addTodoStarted());
      
          console.log('current state:', getState());
      
          // ...
        };
      };
      

      Avec ce qui précède, l’état actuel s’imprimera sur la console.

      Par exemple :

      {loading: true, todos: Array(1), error: null}
      

      Il peut s’avérer utile d’utiliser getState pour gérer les choses différemment en fonction de l’état actuel. Par exemple, si vous souhaitez limiter l’application à quatre éléments de todo à la fois, vous pouvez les renvoyer à partir de la fonction, si l’état contient déjà le nombre maximum d’éléments todo :

      src/actions/index.js

      export const addTodo = ({ title, userId }) => {
        return (dispatch, getState) => {
          const { todos } = getState();
      
          if (todos.length > 4) return;
      
          dispatch(addTodoStarted());
      
          // ...
        };
      };
      

      Compte tenu de ce qui précède, l’app sera limitée à quatre éléments de todo.

      Conclusion

      Au cours de ce tutoriel, vous avez appris à ajouter Redux Thunk à une application React pour pouvoir exécuter des actions de manière asynchrone. Ceci est utile si vous utilisez un store Redux et que vous vous appuyez sur des API externes.

      Si vous souhaitez en savoir plus sur React, consultez notre série Comment coder dans React.js ou consultez notre page thématique React pour des exercices et des projets de programmation.



      Source link