Comment effectuer des actions Redux asynchrones à l'aide de Redux-Thunk

Salutations Habr! Je vous présente la traduction de l'article - Actions Redyn asynchrones utilisant Redux Thunk , auteur - Alligator.io


Par défaut, les actions dans Redux sont synchrones, ce qui est un problème pour une application qui doit interagir avec l'API du serveur ou effectuer d'autres actions asynchrones. Heureusement, Redux nous fournit un middleware , qui se situe entre l'envoi de l'action et le réducteur. Il existe deux des bibliothèques de middleware les plus populaires pour les actions asynchrones dans Redux, il s'agit de Redux Thunk et Redux Saga . Dans cet article, nous examinerons le premier.

Redux Thunk est une bibliothèque de middleware qui vous permet d'appeler le créateur d'actions, en renvoyant une fonction au lieu d'un objet. La fonction prend la méthode de répartition comme argument, de sorte qu'après la fin de l'opération asynchrone, utilisez-la pour répartir l'action synchrone régulière à l'intérieur du corps de la fonction.

Si vous êtes intéressé, Thunk est un concept dans le monde de la programmation lorsqu'une fonction est utilisée pour retarder une opération.

Installation et configuration


Tout d'abord, ajoutez le package redux-thunk à votre projet:

$ yarn add redux-thunk # ,   npm: $ npm install redux-thunk 

Ensuite, ajoutez un middleware lorsque vous créez le magasin de votre application à l'aide de applyMiddleware fourni par Redux:

index.js

 import React from 'react'; import ReactDOM from 'react-dom'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import thunk from 'redux-thunk'; import rootReducer from './reducers'; import App from './App'; //  applyMiddleware,   thunk middleware   const store = createStore(rootReducer, applyMiddleware(thunk)); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ); 

Utilisation principale


En règle générale, Redux-Thunk est utilisé pour les demandes asynchrones vers une API externe, pour recevoir ou enregistrer des données. Redux-Thunk facilite l'envoi d'actions qui suivent le "cycle de vie" d'une demande d'API externe.

Par exemple, nous avons une application à faire régulièrement. Lorsque nous cliquons sur "ajouter une tâche", généralement, la première action sera envoyée, qui signale le début de l'ajout d'une nouvelle tâche. Ensuite, si l'élément todo est correctement créé et renvoyé par le serveur, une autre action est envoyée avec notre nouvel élément todo et l'opération se termine avec succès. Si, pour une raison quelconque, le serveur renvoie une erreur, au lieu d'ajouter une nouvelle tâche, une action avec une erreur s'affiche indiquant que l'opération n'a pas été terminée.

Voyons comment cela peut être implémenté à l'aide de Redux-Thunk. Dans le composant, l'action est envoyée comme d'habitude:

AddTodo.js

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

Dans l'action elle-même, la situation est beaucoup plus intéressante. Ici, nous utiliserons la bibliothèque Axios pour les requêtes ajax. Si vous ne l'avez pas installé, ajoutez-le comme ceci:

 # Yarn $ yarn add axios # npm $ npm install axios --save 

Nous ferons une demande POST à ​​l'adresse - jsonplaceholder.typicode.com/todos :
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 } }); 

Remarquez comment notre créateur d'action addTodo retourne une fonction, au lieu de l'action habituelle de l'objet. Cette fonction prend un argument de répartition du magasin.

Dans le corps de la fonction, nous envoyons d'abord l'action synchrone habituelle, qui signale que nous avons commencé à ajouter une nouvelle tâche à l'aide d'une API externe. En termes simples - la demande a été envoyée au serveur. Ensuite, nous faisons une requête POST au serveur en utilisant Axios. Dans le cas d'une réponse affirmative du serveur, nous envoyons l'action synchronisée en utilisant les données reçues du serveur. Mais en cas d'erreur du serveur, nous envoyons une autre action synchrone avec un message d'erreur.

Lorsque nous utilisons une API vraiment externe (distante), comme le JSONPlaceholder dans notre cas, il est facile de remarquer qu'il y a un délai jusqu'à ce que la réponse du serveur arrive. Mais si vous travaillez avec un serveur local, la réponse peut arriver trop rapidement, vous ne remarquerez donc pas de retard. Donc, pour votre commodité, vous pouvez ajouter un retard artificiel lors du développement:

actions / index.js (morceau de code)

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

Et pour tester un script avec une erreur, vous pouvez directement lancer une erreur:

actions / index.js (morceau de code)

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

Par souci d'exhaustivité, voici un exemple de l'apparence de notre réducteur de tâches afin de traiter le «cycle de vie» complet d'une demande:

réducteurs / todoReducer.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; } } 

getState


La fonction renvoyée par le créateur d'action asynchrone à l'aide de Redux-Thunk prend également la méthode getState comme deuxième argument, ce qui vous permet d'obtenir l'état directement à l'intérieur du créateur d'action:

actions / index.js (morceau de code)

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

Lors de l'exécution de ce code, l'état actuel sera simplement affiché sur la console. Par exemple:

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

L'utilisation de getState peut être très utile lorsque vous devez réagir différemment, selon l'état actuel. Par exemple, si nous avons limité le nombre maximum d'éléments todo à 4, nous pouvons simplement quitter la fonction si cette limite est dépassée:

actions / index.js (morceau de code)

 export const addTodo = ({ title, userId }) => { return (dispatch, getState) => { const { todos } = getState(); if (todos.length >= 4) return; dispatch(addTodoStarted()); // ... }; }; 
Fait amusant - saviez-vous que le code Redux-Thunk ne comprend que 14 lignes? Vous pouvez vérifier par vous - même comment le middleware Redux-Thunk fonctionne sous le capot
Lien vers l'article d'origine - Actions Redux asynchrones utilisant Redux Thunk .

Source: https://habr.com/ru/post/fr483314/


All Articles