我们可以在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 使用步骤

  1. store/index.js入口文件
  2. # 1、引入redux-thunk
  3. import thunkMiddleware from 'redux-thunk'
  4. # 2、引入中间件函数
  5. import { createStore, applyMiddleware } from 'redux'
  6. // 引入reducer函数
  7. import reducer from './reducer.js'
  8. # 3、使用中间件的函数 reduxThunk插入到redux中进行使用
  9. const StoreEnhance = applyMiddleware(thunkMiddleware)
  10. const store = createStore(reducer, StoreEnhance)
  11. // 将store函数导出 提供给业务组件使用
  12. export default store

1.2 在组件中使用

  1. import React, { PureComponent } from 'react';
  2. // 引入connect函数
  3. import { connect } from 'react-redux';
  4. // 引入action
  5. import { incrementAction, addNumberAction, getHomeMultidata } from '../store/actionCreators';
  6. class Home extends PureComponent {
  7. componentDidMount() {
  8. // react-thunk的写法 调用函数 派发一个函数 而不是一个对象 这个函数会在actionCreators中自动进行执行
  9. this.props.getAsyncData()
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <h2>Home组件-使用中间件redux-thunkredux中进行异步请求</h2>
  15. <h2>{ this.props.counter }</h2>
  16. <button onClick={ () => this.props.btn1Click() }>+1</button>
  17. <button onClick={ () => this.props.btn2Click(5) }>+5</button>
  18. </div>
  19. )
  20. }
  21. }
  22. const mapStateToProps = state => {
  23. return {
  24. counter: state.counter
  25. }
  26. }
  27. const mapDispatchToProps = dispatch => {
  28. return {
  29. btn1Click() {
  30. dispatch(incrementAction())
  31. },
  32. btn2Click(num) {
  33. dispatch(addNumberAction(num))
  34. },
  35. // 在redux中获取数据
  36. getAsyncData() {
  37. dispatch(getHomeMultidata)
  38. }
  39. }
  40. }
  41. export default connect(mapStateToProps, mapDispatchToProps)(Home)

1.3 在actionCreator.js中进行定义

  1. import { INCREMENT, ADD_NUMBER, DECREMENT, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants'
  2. import axios from 'axios'
  3. // 自增
  4. export function incrementAction() {
  5. return {
  6. type: INCREMENT,
  7. }
  8. }
  9. // 增加数字
  10. export function addNumberAction(num) {
  11. return {
  12. type: ADD_NUMBER,
  13. num
  14. }
  15. }
  16. // 自减
  17. export const decrementAction = () => {
  18. return {
  19. type: DECREMENT
  20. }
  21. }
  22. // 减少
  23. export const subNumberAction = num => {
  24. return {
  25. type: SUB_NUMBER,
  26. num
  27. }
  28. }
  29. // 轮播图的action
  30. export const changeBannerAction = banner => {
  31. return {
  32. type: CHANGE_BANNER,
  33. banner
  34. }
  35. }
  36. // 推荐的action
  37. export const changeRecommendAction = recommend => {
  38. return {
  39. type: CHANGE_RECOMMEND,
  40. recommend
  41. }
  42. }
  43. // 获取异步数据 redux-thunk的逻辑就是 拦截action 在action中发送相应的网络请求 同时这个函数可以将dispatch、getState传递过来
  44. export const getHomeMultidata = (dispatch, getState) => {
  45. axios({ url: 'http://123.207.32.32:8000/home/multidata' }).then(res => {
  46. const banner = res.data.data.banner.list
  47. const recommend = res.data.data.recommend.list
  48. // 获取了异步数据后 再次进行派发事件 直接使用上面的action
  49. dispatch(changeBannerAction(banner))
  50. // 再次派发action
  51. dispatch(changeRecommendAction(recommend))
  52. })
  53. // 进行异步操作
  54. }

1.4 reducer.js文件中使用

  1. // 常量
  2. import { INCREMENT, DECREMENT, ADD_NUMBER, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants';
  3. // 需要redux管理的初始化状态
  4. const initialState = {
  5. counter: 100,
  6. banner: [],
  7. recommend: []
  8. }
  9. // 纯函数
  10. function reducer(state = initialState, action) {
  11. switch(action.type) {
  12. case INCREMENT:
  13. return {...state, counter: state.counter + 1 }
  14. case ADD_NUMBER:
  15. return {...state, counter: state.counter + action.num }
  16. case DECREMENT:
  17. return {...state, counter: state.counter - 1 }
  18. case SUB_NUMBER:
  19. return {...state, counter: state.counter - action.num }
  20. // 修改banner的数据
  21. case CHANGE_BANNER:
  22. return {...state, banner: action.banner }
  23. // 修改recommend的数据
  24. case CHANGE_RECOMMEND:
  25. return {...state, recommend: action.recommend }
  26. default:
  27. return state
  28. }
  29. }
  30. // 将纯函数进行导出
  31. 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函数进行拆分,将其按照相应的模块进行拆分,那么我们的逻辑就可以分开了。 :::

  1. // reducer函数逻辑的拆解与拆分。
  2. // 常量
  3. import { INCREMENT, DECREMENT, ADD_NUMBER, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND } from './constants';
  4. // 全局的initialCountState数据
  5. const initialCountState = {
  6. counter: 100
  7. };
  8. // 定义reducer函数
  9. function counterReducer(state = initialCountState, action) {
  10. switch(action.type) {
  11. case INCREMENT:
  12. return {...state, counter: state.counter + 1 }
  13. case ADD_NUMBER:
  14. return {...state, counter: state.counter + action.num }
  15. case DECREMENT:
  16. return {...state, counter: state.counter - 1 }
  17. case SUB_NUMBER:
  18. return {...state, counter: state.counter - action.num }
  19. default:
  20. return state
  21. }
  22. }
  23. // 全局的initialHomeState数据
  24. const initialHomeState = {
  25. banner: [],
  26. recommend: []
  27. };
  28. // 定义homeReducer纯函数
  29. function homeReducer(state = initialHomeState, action) {
  30. switch(action.type) {
  31. // 修改banner的数据
  32. case CHANGE_BANNER:
  33. return {...state, banner: action.banner }
  34. // 修改recommend的数据
  35. case CHANGE_RECOMMEND:
  36. return {...state, recommend: action.recommend }
  37. default:
  38. return state
  39. }
  40. }
  41. // 需要将上述两个reducer纯函数进行相应的合并处理 我们自己对其进行的拆分。
  42. function reducer(state = {}, action) {
  43. return {
  44. counterInfo: counterReducer(counterInfo, action),
  45. homeInfo: homeReducer(homeInfo, action)
  46. }
  47. }
  48. // 我们可以使用redux提供给我们的combineReducer函数
  49. import { combineReducers } from "redux";
  50. // combineReducer函数接收一个对象作为参数。
  51. const reducer = combineReducer({
  52. counterInfo: counterReducer,
  53. homeInfo: homeReducer
  54. })
  55. // 将纯函数进行导出
  56. export default reducer;
  57. // 上述reducer的代码合并以后,需要注意的是在我们的业务组件中访问的时候,需要使用state.counterInfo.counter和state.homeInfo.banner与state.homeInfo.recommend的方式获取最新的全局的状态。