Redux中间件用法详解-redux-thunk/redux-logger/redux-devtools-extension
参考:https://blog.csdn.net/hsany330/article/details/105951197
前面我们有说过,redux由三大核心组成部分:reducer、action、store。
其中,Reducer是一个纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
所以就有了redux中间件。
默认情况下,Redux 自身只能处理同步数据流。但是在实际项目开发中,状态的更新、获取,通常是使用异步操作来实现。
- 问题:如何在 Redux 中进行异步操作呢?
- 回答:通过 Redux 中间件机制来实现
1.redux中间件概念
- 中间件,可以理解为处理一个功能的中间环节
- 中间件的优势:可以串联、组合,在一个项目中使用多个中间件
- Redux 中间件用来处理 状态 更新,也就是在 状态 更新的过程中,执行一系列的相应操作
dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
换言之,中间件都是对store.dispatch()的增强
2.中间件的触发时机
- Redux 中间件执行时机:在 dispatching action 和 到达 reducer 之间。
- 没有中间件:
dispatch(action) => reducer
- 使用中间件:
dispatch(action) => 执行中间件代码 => reducer
原理:封装了 redux 自己的 dispatch 方法
没有中间件:store.dispatch() 就是 Redux 库自己提供的 dispatch 方法,用来发起状态更新
使用中间件:store.dispatch() 就是中间件封装处理后的 dispatch,但是,最终一定会调用 Redux 自己的 dispatch 方法发起状态更新
redux的异步数据流动过程:
3.中间件的原理
Redux 中间件原理:创建一个函数,包装 store.dispatch,使用新创建的函数作为新的 dispatch
比如下图,logger 就是一个中间件,使用该中间件后 store.dispatch 就是包装后的新 dispatch
中间件修改了 store.dispatch,在分发动作和到达 reducer 之间提供了扩展
redux 中间件采用了 洋葱模型 来实现
-
自己实现记录日志的 redux 中间件:
// 简化写法: // store 表示:redux 的 store // next 表示:下一个中间件,如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数) // action 表示:要分发的动作 const logger = store => next => action => { console.log('prev state:', store.getState()) // 更新前的状态 // 记录日志代码 console.log('dispatching', action) // 如果只使用了一个中间件: // 那么,next 就表示原始的 dispatch // 也就是:logger中间件包装了 store.dispatch let result = next(action) // 上面 next 代码执行后,redux 状态就已经更新了,所以,再 getState() 拿到的就是更新后的最新状态值 // 记录日志代码 console.log('next state', store.getState()) // 更新后的状态 return result } // 完整写法: const logger = store => { return next => { return action => { // 中间件代码写在这个位置: } } }
4.中间件的用法
redux中间件需要在createStore()生成store时注册:
import { applyMiddleware, createStore, combineReducers } from 'redux'; import thunk from 'redux-thunk'; import reducer1 from '../reducer/reducer1'; import reducer2 from '../reducer/reducer2'; const reducers = combineReducers(reducer1); const store = createStore( reducers, applyMiddleware(thunk) ); export default store;
redux常见的中间件有redux-thunk,redux-logger,redux-saga等。
redux-devtools-extension中间件
目标:能够使用chrome开发者工具调试跟踪redux状态
内容:
redux-devtools-exension 文档:https://www.npmjs.com/package/redux-devtools-extension
先给 Chrome 浏览器安装 redux 开发者工具,然后,就可以查看 Redux 状态了
步骤:
1.安装: yarn add redux-devtools-extension
2.从该中间件中导入 composeWithDevTools 函数
3.调用该函数,将 applyMiddleware() 作为参数传入
4.打开 Chrome 浏览器的 redux 开发者工具并使用
import thunk from 'redux-thunk' import { composeWithDevTools } from 'redux-devtools-extension' const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk))) export default store
redux-thunk中间件
源码解读,原理(redux-thunk@2.3.0源码:https://github.com/reduxjs/redux-thunk/blob/v2.3.0/src/index.js):
function createThunkMiddleware(extraArgument) {
// Redux 中间件的写法:const myMiddleware = store => next => action => { /* 此处写 中间件 的代码 */ }
return ({ dispatch, getState }) => (next) => (action) => {
// redux-thunk 的核心代码:
// 判断 action 的类型是不是函数
// 如果是函数,就调用该函数(action),并且传入了 dispatch 和 getState
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 如果不是函数,就调用下一个中间件(next),将 action 传递过去
// 如果没有其他中间件,那么,此处的 next 指的就是:Redux 自己的 dispatch 方法
return next(action);
};
}
// 所以,在使用了 redux-thunk 中间件以后,那么,redux 就既可以处理 对象形式的 action 又可以处理 函数形式的 action 了
// 1 处理对象形式的 action
dispatch({ type: 'todos/clearAll' }) // 对应上面第 14 行代码
// 2 处理函数型的 action
export const clearAllAsync = () => {
return dispatch => {
// 在此处,执行异步操作
setTimeout(() => {
// 异步操作完成后,如果想要修改 redux 中的状态,就必须要
// 分发一个 对象形式的 action(同步的 action)
dispatch({ type: types.CLEAR_ALL })
}, 1000)
}
}
dispatch(clearAllAsync()) // 对应上面第 8、9 行代码
目标:能够使用redux-thunk中间件处理异步操作
内容:
redux-thunk 中间件可以处理函数形式的 action。因此,在函数形式的 action 中就可以执行异步操作
语法:
thunk action 是一个函数
函数包含两个参数:1 dispatch 2 getState
// 函数形式的 action const thunkAction = () => { return (dispatch, getState) => {} } // 解释: const thunkAction = () => { // 注意:此处返回的是一个函数,返回的函数有两个参数: // 第一个参数:dispatch 函数,用来分发 action // 第二个参数:getState 函数,用来获取 redux 状态 return (dispatch, getState) => { setTimeout(() => { // 执行异步操作 // 在异步操作成功后,可以继续分发对象形式的 action 来更新状态 }, 1000) } }
使用 redux-thunk 中间件前后对比:
1.不使用 redux-thunk 中间件,action 只能是一个对象
// 1 普通 action 对象 { type: 'counter/increment' } dispatch({ type: 'counter/increment' }) // 2 action creator const increment = payload => ({ type: 'counter/increment', payload }) dispatch(increment(2))
2.使用 redux-thunk 中间件后,action 既可以是对象,又可以是函数
// 1 对象: // 使用 action creator 返回对象 const increment = payload => ({ type: 'counter/increment', payload }) // 分发同步 action dispatch(increment(2)) // 2 函数: // 使用 action creator 返回函数 const incrementAsync = () => { return (dispatch, getState) => { // ... 执行异步操作代码 } } // 分发异步 action dispatch(incrementAsync())
步骤:
- 安装:
yarn add redux-thunk
- 导入 redux-thunk
- 将 thunk 添加到 applyMiddleware 函数的参数(中间件列表)中
- 创建函数形式的 action,在函数中执行异步操作
核心代码:
store/index.js 中:
// 导入 thunk 中间件 import thunk from 'redux-thunk' // 将 thunk 添加到中间件列表中 // 知道:如果中间件中使用 logger 中间件,logger 中间件应该出现在 applyMiddleware 的最后一个参数 const store = createStore(rootReducer, applyMiddleware(thunk, logger))
actions/index.js 中:
export const clearAllAsync = () => { return (dispatch) => { // 处理异步的代码:1 秒后再清理已完成任务 setTimeout(() => { dispatch(clearAll()) }, 1000) } }
App.js 中:
import { clearTodoAsync } from '../store/actions/todos' const TodoFooter = () => { return ( // ... <button className="clear-completed" onClick={() => dispatch(clearTodoAsync())} > Clear completed </button> ) }
redux-logger中间件
目标:能够使用redux-logger中间件记录日志
步骤:
1.安装:yarn add redux-logger
2.导入 redux-logger 中间件
3.从 redux 中导入 applyMiddleware 函数
4.调用 applyMiddleware() 并传入 logger 中间件作为参数
5.将 applyMiddleware() 调用作为 createStore 函数的第二个参数
然后,调用 store.dispatch() 查看 console 中 logger 中间件记录的日志信息
核心代码:
store/index.js 中:
import { createStore, applyMiddleware } from 'redux' import logger from 'redux-logger' import rootReducer from './reducers' const store = createStore(rootReducer, applyMiddleware(logger))
当然redux的中间件有很多,本文只讲了其中的一部分。比如redux-saga也是解决redux中异步问题的中间件,redux-thunk是把异步操作放在action里操作,而redux-saga的设计思想是把异步操作单独拆分出来放在一个文件里管理(面试常问)。
其他中间件,像redux-promise、redux-saga等,此处篇幅有限,改日再讲。此处先略过。有兴趣的可参考:https://blog.csdn.net/yrqlyq/article/details/119243072
好了,吹水到此。编程使我快乐,快乐让我吹水。欢迎留言互动互粉!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具