我们可以在react组件中发送相应的网络请求,但是因为我们的数据是保存在我们的redux中的,那么,我们是否可以可以考虑将我们的异步请求的这个过程放在redux中进行呢?事实上,这是可以的,那就需要使用到我们的中间件,中间件的原理:劫持dispatch派发的action,在action中发送异步的网络请求,再派发相应的action,将请求的结果放在redux中,这就是redux中发送网路请求的一个过程。
1、redux-thunk中间件的使用原理
使用原理: 1、react-redux提供了一个函数入口,叫做applyMiddleware,可以将我们要使用的所有的中间件加入到这个函数里面,当作参数进行使用。 2、将应用插件函数的返回值,当作参数传递给createStore函数中,当作参数进行使用。 3、在业务组件挂载完毕后中派发一个函数,把这个函数定义在actionCreators中,不需要进行调用,thunk函数会自动调用,并且将dispatch和getState方法传入,在函数里面利用axios发送网络请求,获取数据,将获取的数据重新进行派发dispatch。
1.1 使用步骤
store/index.js入口文件# 1、引入redux-thunkimport thunkMiddleware from 'redux-thunk'# 2、引入中间件函数import { createStore, applyMiddleware } from 'redux'// 引入reducer函数import reducer from './reducer.js'# 3、使用中间件的函数 将reduxThunk插入到redux中进行使用const StoreEnhance = applyMiddleware(thunkMiddleware)const store = createStore(reducer, StoreEnhance)// 将store函数导出 提供给业务组件使用export default store
1.2 在组件中使用
import React, { PureComponent } from 'react';// 引入connect函数import { connect } from 'react-redux';// 引入actionimport { incrementAction, addNumberAction, getHomeMultidata } from '../store/actionCreators';class Home extends PureComponent {componentDidMount() {// react-thunk的写法 调用函数 派发一个函数 而不是一个对象 这个函数会在actionCreators中自动进行执行this.props.getAsyncData()}render() {return (<div><h2>Home组件-使用中间件redux-thunk在redux中进行异步请求</h2><h2>{ this.props.counter }</h2><button onClick={ () => this.props.btn1Click() }>+1</button><button onClick={ () => this.props.btn2Click(5) }>+5</button></div>)}}const mapStateToProps = state => {return {counter: state.counter}}const mapDispatchToProps = dispatch => {return {btn1Click() {dispatch(incrementAction())},btn2Click(num) {dispatch(addNumberAction(num))},// 在redux中获取数据getAsyncData() {dispatch(getHomeMultidata)}}}export default connect(mapStateToProps, mapDispatchToProps)(Home)
1.3 在actionCreator.js中进行定义
import { INCREMENT, ADD_NUMBER, DECREMENT, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants'import axios from 'axios'// 自增export function incrementAction() {return {type: INCREMENT,}}// 增加数字export function addNumberAction(num) {return {type: ADD_NUMBER,num}}// 自减export const decrementAction = () => {return {type: DECREMENT}}// 减少export const subNumberAction = num => {return {type: SUB_NUMBER,num}}// 轮播图的actionexport const changeBannerAction = banner => {return {type: CHANGE_BANNER,banner}}// 推荐的actionexport const changeRecommendAction = recommend => {return {type: CHANGE_RECOMMEND,recommend}}// 获取异步数据 redux-thunk的逻辑就是 拦截action 在action中发送相应的网络请求 同时这个函数可以将dispatch、getState传递过来export const getHomeMultidata = (dispatch, getState) => {axios({ url: 'http://123.207.32.32:8000/home/multidata' }).then(res => {const banner = res.data.data.banner.listconst recommend = res.data.data.recommend.list// 获取了异步数据后 再次进行派发事件 直接使用上面的actiondispatch(changeBannerAction(banner))// 再次派发actiondispatch(changeRecommendAction(recommend))})// 进行异步操作}
1.4 reducer.js文件中使用
// 常量import { INCREMENT, DECREMENT, ADD_NUMBER, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants';// 需要redux管理的初始化状态const initialState = {counter: 100,banner: [],recommend: []}// 纯函数function reducer(state = initialState, action) {switch(action.type) {case INCREMENT:return {...state, counter: state.counter + 1 }case ADD_NUMBER:return {...state, counter: state.counter + action.num }case DECREMENT:return {...state, counter: state.counter - 1 }case SUB_NUMBER:return {...state, counter: state.counter - action.num }// 修改banner的数据case CHANGE_BANNER:return {...state, banner: action.banner }// 修改recommend的数据case CHANGE_RECOMMEND:return {...state, recommend: action.recommend }default:return state}}// 将纯函数进行导出export default reducer;
:::info 总结与整理redux-thunk的使用技巧:
- 首先在redux的配置文件中对redux进行配置,react预留一个applyMiddleware的接口,提供给中间件的使用着,将中间件以参数的形式传入函数,函数调用后的结果以参数的形式,放入到createStore函数中。createStore函数中的第一个参数是reducer纯函数。
- redux-thunk中间件的原理就是拦截actions,之前我们派发action的时候,就是派发一个对象的形式,现在我们可以直接派发一个函数了,这个函数定义在actionCreators中,我们的redux内部会自动回调这个函数,并且将dispatch、getState函数作为参数传到我们自定的函数中。
- 当redux对我们的函数进行回调的时候,我们就可以在这个函数中进行相应的异步操作,获取到异步操作的结构后,我们任然可以正常dispatch相应的action了。我们异步的数据可以作为action的参数传递给我们的reducer函数。reducer内部就会对这个函数进行相应的处理。直到完成逻辑的任务。 :::
2、reducer函数的拆分
:::info 在上述的开发中,我们可以看出,我们组件需要的所有的状态,全部置于一个reducer中,这样是不规范与不合理的,因为随着我们项目的扩大,我们项目需要管理的状态越来越多,将我们所有状态的处理逻辑放置于一个reducer函数中是不合理的。所以我们需要将reduce函数进行拆分,将其按照相应的模块进行拆分,那么我们的逻辑就可以分开了。 :::
// reducer函数逻辑的拆解与拆分。// 常量import { INCREMENT, DECREMENT, ADD_NUMBER, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants';// 全局的initialCountState数据const initialCountState = {counter: 100};// 定义reducer函数function counterReducer(state = initialCountState, action) {switch(action.type) {case INCREMENT:return {...state, counter: state.counter + 1 }case ADD_NUMBER:return {...state, counter: state.counter + action.num }case DECREMENT:return {...state, counter: state.counter - 1 }case SUB_NUMBER:return {...state, counter: state.counter - action.num }default:return state}}// 全局的initialHomeState数据const initialHomeState = {banner: [],recommend: []};// 定义homeReducer纯函数function homeReducer(state = initialHomeState, action) {switch(action.type) {// 修改banner的数据case CHANGE_BANNER:return {...state, banner: action.banner }// 修改recommend的数据case CHANGE_RECOMMEND:return {...state, recommend: action.recommend }default:return state}}// 需要将上述两个reducer纯函数进行相应的合并处理 我们自己对其进行的拆分。function reducer(state = {}, action) {return {counterInfo: counterReducer(counterInfo, action),homeInfo: homeReducer(homeInfo, action)}}// 我们可以使用redux提供给我们的combineReducer函数import { combineReducers } from "redux";// combineReducer函数接收一个对象作为参数。const reducer = combineReducer({counterInfo: counterReducer,homeInfo: homeReducer})// 将纯函数进行导出export default reducer;// 上述reducer的代码合并以后,需要注意的是在我们的业务组件中访问的时候,需要使用state.counterInfo.counter和state.homeInfo.banner与state.homeInfo.recommend的方式获取最新的全局的状态。
