如何使用Redux-Thunk执行异步Redux操作

问候哈伯! 我向您介绍文章的翻译- 使用Redux Thunk进行异步Redux操作 ,作者-Alligator.io


默认情况下,Redux中的操作是同步的,这对于需要与服务器API交互或执行其他异步操作的应用程序来说是个问题。 幸运的是,Redux为我们提供了中间件之类的东西,它介于动作分派和reducer之间。 Redux中有两个最受欢迎的异步操作中间件库,分别是Redux ThunkRedux Saga 在这篇文章中,我们将考虑第一个。

Redux Thunk是一个中间件库,可让您调用动作创建者,返回函数而不是对象。 该函数以dispatch方法作为参数,因此异步操作完成后,可使用它在函数体内调度常规的同步操作。

如果您有兴趣,那么Thunk是编程世界中的一个概念,当函数用于延迟操作时。

安装与设定


首先,将redux-thunk软件包添加到您的项目中:

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

然后,在使用Redux提供的applyMiddleware创建应用程序存储时添加中间件:

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

主要用途


通常,Redux-Thunk用于对外部API的异步请求,以检索或存储数据。 Redux-Thunk使得分发遵循外部API请求“生命周期”的操作变得容易。

例如,我们有一个常规的待办事项应用程序。 通常,当我们单击“添加待办事项”时,将调度第一个操作,该操作报告开始添加新的待办事项。 然后,如果成功创建了todo元素并由服​​务器返回,则使用我们的新todo元素调度另一个动作,并且操作成功完成。 如果由于某种原因服务器返回了错误,则将显示操作未完成的错误,而不是添加新的待办事项。

让我们看看如何使用Redux-Thunk实现它。 在组件中,该操作将照常分派:

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

在行动本身中,情况要有趣得多。 在这里,我们将使用Axios库处理ajax请求。 如果您尚未安装,请按以下方式添加:

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

我们将向该地址发出POST请求-jsonplaceholder.typicode.com/todos
动作/ 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 } }); 

请注意,我们的addTodo动作创建者如何返回一个函数,而不是对象的通常动作。 此函数从存储中获取调度参数。

在函数主体内部,我们首先调度通常的同步操作,该操作报告我们开始使用外部API添加新的待办事项。 简单来说-请求已发送到服务器。 然后,我们实际上使用Axios向服务器发出POST请求。 如果服务器发出肯定答复,我们将使用从服务器接收的数据来调度同步操作。 但是,如果发生服务器错误,我们将分派另一个同步操作并显示错误消息。

当我们使用真正的外部(远程)API时(例如本中的JSONPlaceholder ),很容易注意到在延迟到服务器响应到达之前会有延迟。 但是,如果您使用的是本地服务器,答案可能会太快,因此您不会注意到延迟。 因此,为方便起见,您可以在开发时添加人为延迟:

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

为了测试有错误的脚本,您可以直接抛出错误:

actions / index.js(代码段)

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

为了完整起见,下面是一个示例,说明我们的待办事项简化程序如何处理请求的完整“生命周期”:

减速器/ 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


异步操作创建者使用Redux-Thunk返回的函数还采用getState方法作为第二个参数,这使您可以直接在操作创建者内部获取状态:

actions / index.js(代码段)

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

执行此代码时,当前状态将仅输出到控制台。 例如:

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

当需要根据当前状态做出不同反应时,使用getState确实非常有用。 例如,如果我们将todo元素的最大数量限制为4,那么如果超出此限制,我们可以简单地退出该函数:

actions / index.js(代码段)

 export const addTodo = ({ title, userId }) => { return (dispatch, getState) => { const { todos } = getState(); if (todos.length >= 4) return; dispatch(addTodoStarted()); // ... }; }; 
有趣的事实 -您知道Redux-Thunk代码仅包含14行吗? 您可以亲自检查Redux-Thunk中间件的工作原理
链接到原始文章- 使用Redux Thunk的异步Redux操作

Source: https://habr.com/ru/post/zh-CN483314/


All Articles