So führen Sie asynchrone Redux-Aktionen mit Redux-Thunk durch

Grüße Habr! Ich präsentiere Ihnen die Übersetzung des Artikels - Asynchrone Redux-Aktionen mit Redux Thunk , Autor - Alligator.io


Standardmäßig sind Redux-Aktionen synchron. Dies ist ein Problem für eine Anwendung, die mit der Server-API interagieren oder andere asynchrone Aktionen ausführen muss. Glücklicherweise stellt uns Redux Middleware zur Verfügung , die zwischen dem Aktionsversand und dem Reduzierer steht. Es gibt zwei der beliebtesten Middleware-Bibliotheken für asynchrone Aktionen in Redux: Redux Thunk und Redux Saga . In diesem Beitrag werden wir den ersten betrachten.

Redux Thunk ist eine Middleware-Bibliothek, mit der Sie Action Creator aufrufen können, indem Sie eine Funktion anstelle eines Objekts zurückgeben. Die Funktion verwendet die Dispatch-Methode als Argument, sodass Sie nach Abschluss der asynchronen Operation die reguläre Synchronaktion innerhalb des Funktionskörpers auslösen können.

Wenn Sie interessiert sind, dann ist Thunk ein Begriff in der Programmierwelt, wenn eine Funktion verwendet wird, um eine Operation zu verzögern.

Installation und Einrichtung


Fügen Sie Ihrem Projekt zunächst das Paket redux-thunk hinzu :

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

Fügen Sie dann Middleware hinzu, wenn Sie den Store Ihrer Anwendung mit der von Redux bereitgestellten applyMiddleware erstellen:

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

Hauptgebrauch


In der Regel wird Redux-Thunk für asynchrone Anforderungen an eine externe API zum Abrufen oder Speichern von Daten verwendet. Redux-Thunk erleichtert das Versenden von Aktionen, die dem "Lebenszyklus" einer externen API-Anforderung folgen.

Zum Beispiel haben wir eine reguläre ToDo-Anwendung. Wenn wir auf "Aufgabe hinzufügen" klicken, wird normalerweise die erste Aktion ausgelöst, die über den Beginn des Hinzufügens einer neuen Aufgabe berichtet. Wenn das todo-Element erfolgreich erstellt und vom Server zurückgegeben wurde, wird eine weitere Aktion mit unserem neuen todo-Element ausgelöst, und der Vorgang wird erfolgreich abgeschlossen. Wenn der Server aus irgendeinem Grund einen Fehler zurückgibt, wird anstelle eines neuen Vorgangs eine Aktion mit dem Fehler angezeigt, dass der Vorgang nicht abgeschlossen wurde.

Mal sehen, wie das mit Redux-Thunk umgesetzt werden kann. In der Komponente wird die Aktion wie gewohnt ausgelöst:

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

In der Handlung selbst ist die Situation viel interessanter. Hier verwenden wir die Axios- Bibliothek für Ajax-Anfragen. Wenn Sie es nicht installiert haben, fügen Sie es folgendermaßen hinzu:

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

Wir senden eine POST-Anfrage an die folgende 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 } }); 

Beachten Sie, dass unser Aktionsersteller addTodo anstelle der üblichen Aktion des Objekts eine Funktion zurückgibt. Diese Funktion übernimmt ein Versandargument aus dem Speicher.

Im Hauptteil der Funktion wird zunächst die übliche Synchronaktion ausgelöst, bei der gemeldet wird, dass mit dem Hinzufügen eines neuen Vorgangs mithilfe einer externen API begonnen wurde. In einfachen Worten - die Anfrage wurde an den Server gesendet. Anschließend stellen wir mithilfe von Axios eine POST-Anforderung an den Server. Im Falle einer bejahenden Antwort vom Server versenden wir die synchronisierte Aktion unter Verwendung der vom Server empfangenen Daten. Im Falle eines Fehlers vom Server senden wir jedoch eine weitere Synchronaktion mit einer Fehlermeldung.

Wenn wir eine wirklich externe (entfernte) API wie den JSONPlaceholder in unserem Fall verwenden, ist es leicht zu bemerken, dass es eine Verzögerung gibt, bis eine Antwort vom Server eintrifft. Wenn Sie jedoch mit einem lokalen Server arbeiten, erfolgt die Antwort möglicherweise zu schnell, sodass Sie keine Verzögerung bemerken. Für Ihre Bequemlichkeit können Sie also künstliche Verzögerungen beim Entwickeln hinzufügen:

actions / index.js (Stück 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)); }); }; }; 

Und um ein fehlerhaftes Skript zu testen, können Sie direkt einen Fehler auslösen:

actions / index.js (Stück 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)); }); }; }; 

Der Vollständigkeit halber ist hier ein Beispiel, wie unser ToDo-Reduzierer aussehen könnte, um den gesamten „Lebenszyklus“ einer Anfrage zu verarbeiten:

Reduzierstücke / 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


Die vom Ersteller der asynchronen Aktion mit Redux-Thunk zurückgegebene Funktion verwendet auch die Methode getState als zweites Argument, mit der Sie den Status direkt im Ersteller der Aktion abrufen können:

actions / index.js (Stück Code)

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

Bei der Ausführung dieses Codes wird der aktuelle Status einfach auf der Konsole ausgegeben. Zum Beispiel:

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

Die Verwendung von getState kann sehr nützlich sein, wenn Sie je nach aktuellem Status unterschiedlich reagieren müssen. Wenn wir zum Beispiel die maximale Anzahl von Aufgabenelementen auf 4 begrenzt haben, können wir die Funktion einfach beenden, wenn diese Grenze überschritten wird:

actions / index.js (Stück Code)

 export const addTodo = ({ title, userId }) => { return (dispatch, getState) => { const { todos } = getState(); if (todos.length >= 4) return; dispatch(addTodoStarted()); // ... }; }; 
Wussten Sie, dass der Redux-Thunk-Code nur aus 14 Zeilen besteht? Sie können selbst überprüfen, wie Redux-Thunk-Middleware unter der Haube funktioniert
Link zum Originalartikel - Asynchrone Redux-Aktionen mit Redux Thunk .

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


All Articles