理解 React 中的 Redux-Thunk

Thunk 是一个逻辑编程概念。你可以用来处理推迟任何事件的计算或者评估的函数,并且 React-Thunk 可以有效地充当应用程序的单独线程。
Redux Thunk 是一个中间件,它允许 Redux 返回函数而不是 actions。这就允许你在延迟处理 actions 的时候结合 promises 使用。
该中间件的主要应用包括处理潜在的异步 actions 操作,例如使用 Axios 发送一个 GET 请求。
借助 Redux Thunk,我们可以异步 dispatch 这些操作并返回 promise 处理的结果。
下面我们来实操下:
设置工作环境
假设你已经通过 create-react-app 生成了一个 redux 项目,参考 React Js 中创建和使用 Redux Store。通过 npm install redux-thunk --save 或者 yarn add redux-thunk 进行安装。
然后,我们可以使用 applyMiddleware() 开启:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
为什么是 Redux Thunk?
如果你熟悉 Redux,你将会了解相关重要概念:actions, actions creators, reducers 和 middleware。
Redux store 只允许同步 dispatch actions,并且一个 Redux store 中不会有任何异步逻辑。它只会明白怎么同步去 dispatch 事件并更新 state。
请注意,Reducer 是一个纯函数;因此它不能用于处理 API 调用。它不应该造成副作用,也不应该直接改变 state。
在 React 中,你不应该直接更改 state。而是,使用 setState 去更新一个对象的 state 状态。
Redux 使用 actions 和 reducers 去更新你应用的 state。使用这两个可以让人们轻松了解数据如何流动以及 state 何时发生变化。
Redux 首先复制 state,然后重写你想更改 state 的值。
return {
...state,
zip: MOR0O0
}
为了让事情简单,Redux-thunk 是一个中间件,使用户能够使用异步函数代替 API 调用。
action 对象应该被返回,因为 action 是一个对象。Redux-thunk 允许一个 action creator 返回一个函数!现在,我们可以做任何异步工作。
Redux-Thunk 就是一小片代码,如下:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if(typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
Thunk 中间件到底是什么
Redux 中间件允许你拦截每个发送到 reducer 的 action,并更改或者取消 action。
中间件可以帮助你进行日志记录、错误报告、异步请求等。
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
const reducer = (state = 0, action) => {
switch(action.type) {
case "INCREMENT":
return state + action.payload;
case "DECREMENT":
return state - action.payload;
default;
return state;
}
};
const store = createStore(reducer);
store.subscribe(() => {
console.log("current state", store.getState());
});
store.dispatch({
type: "INCREMENT",
payload: 1
});
store.dispatch({
type: "INCREMENT",
payload: 5
});
store.dispatch({
type: "DECREMENT",
payload: 2
});
createStore 函数包含三个参数:
- 第一个参数
reducer- 必填 - 第二个参数是
state初始值 - 可选 - 第三个参数是中间件 - 可选
由于嵌套函数的特定语法,createStore 函数会根据参数的类型自动确定传递的参数是中间件。
LoggerMiddleware 如下:
const loggerMiddleware = (store) => (next) => (action) => {
console.log("action", action);
next(action);
}
在控制台上,你将会看到下面的输出:

如上所示,中间件会在 action 被 dispatch 前调用。
怎么使用 Redux Thunk: 构建一个购物车

在本教程中,我们将使用 Redux Thunk 开发一个简单的购物车功能,更好地明白 Thunk 怎么工作。
为了连接 Redux store,我们在 products.json 文件中模拟些数据:
// product.json
[
{"id": 1, "title": "Strawberry ice-cream", "price": 100.01, "inventory": 2},
{"id": 2, "title": "Gucci T-Shirt Blue", "price": 15.99, "inventory": 10},
{"id": 3, "title": "Vulton XCX - Sucker ", "price": 29.99, "inventory": 5}
]
一旦 Redux Thunk 被安装并引入到项目 applyMiddleware(thunk),你就可以异步派发 actions 了。
我们创建购物车的 actions:
import shop from '../api/shop';
import * as types from '../constants/ActionTypes';
const receiveProducts = products => ({
type: types.RECEIVE_PRODUCTS, products
})
export const getAllProducts = () => dispatch => {
shop.getProducts(products => {
dispatch(receiveProducts(products))
})
}
const addToCartUnsafe = productId => ({
type: types.ADD_TO_CART, productid
})
export const addToCart = productId => (dispatch, getState) => {
if(getState().products.byId[productId].inventory > 0) {
dispatch(addToCartUnsafe(productId))
}
}
export const checkout = products => (dispatch, getState) => {
const { cart } = getState()
dispatch({
type: types.CHECKOUT_REQUEST
})
shop.buyProducts(products, () => {
dispatch.buyProducts(products, () => {
dispatch({
type: types.CHECKOUT_SUCCESS, cart
})
})
})
}
而且因为一直手动编写这些对象有些烦人(更不用说容易出错)。
Redux 有 action types/creators 的概念来消除这些事情:
export const ADD_TO_CART = 'ADD_TO_CART'
export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST'
export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'
export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'
export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'
接下来,我们首先导入每个 action 和钩子。我们派发 actions,然后访问 store 中的数据:
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import reducer from './reducers';
import { getAllProducts } from './actions';
import App from './containers/App';
const middleware = [ thunk ];
if(process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
store.dispatch(getAllProducts());
render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('root')
);
确保你已经将 thunk 放在 applyMiddleware 中,否则它不起效。
Redux-Thunk 幕后怎么工作
关于 redux-thunk 的全部代码如下:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// This gets called for every action you dispatch
// If it's a function, call it
if(typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// Otherwise, just continue processing this action as usual
return next(action);
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
安装并应用了 redux-thunk 中间件后,你派发的所有 actions 都会经过上面代码流程。
当一个 action 是一个函数,它会被调用,否则它会被传递给下一个中间件或者 Redux 本身。
参考文件
转载自:https://juejin.cn/post/7253251884525748279