我们可以在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-thunk
import 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';
// 引入action
import { 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
}
}
// 轮播图的action
export const changeBannerAction = banner => {
return {
type: CHANGE_BANNER,
banner
}
}
// 推荐的action
export 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.list
const recommend = res.data.data.recommend.list
// 获取了异步数据后 再次进行派发事件 直接使用上面的action
dispatch(changeBannerAction(banner))
// 再次派发action
dispatch(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的方式获取最新的全局的状态。