• Redux中进行异步操作(网络请求)的方案


    Redux中的异步操作

    在之前简单的案例中,redux中保存的counter是一个本地定义的数据

    我们可以直接通过同步的操作来dispatch action,state就会被立即更新。

    但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。

    在之前学习网络请求的时候我们讲过,发生网络请求我们有两种方案, 可以直接在组件的钩子函数中发送网络请求, 再将数据存放到store中; 也可以直接在store中发生网络请求


    组件中进行异步操作

    网络请求可以在class组件的生命周期函数componentDidMount中发送,所以我们可以有这样的结构:

    在这里插入图片描述

    我现在完成如下案例操作:

    创建一个组件Category, 在该组件中发送网络请求, 获取banners和recommends的数据;

    在About组件中展示banners和recommends的数据;

    首先需要创建要派发的action, 以及对应的reducer

    // store/actionCreators.jsx
    
    import { CHANGE_BANNERS, CHANGE_RECOMMENDS } from "./constants"
    
    export const changeBannersAction = (banners) => ({
      type: CHANGE_BANNERS,
      banners
    })
    export const changeRecommendsAction = (recommends) => ({
      type: CHANGE_RECOMMENDS,
      recommends
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    // store/reducer.jsx
    
    import { CHANGE_BANNERS, CHANGE_RECOMMENDS } from "./constants"
    
    const initialState = {
      banners: [],
      recommends: []
    }
    
    export default function reducer(state = initialState, action) {
      switch(action.type) {
        case CHANGE_BANNERS:
          return {...state, [banners: action.banners}
        case CHANGE_RECOMMENDS:
          return {...state, recommends: action.recommends}
        default: 
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在Categroy组件中发送网络请求, 并将store中的banners和recommends修改为网络请求后的数据

    import React, { PureComponent } from 'react'
    import axios from 'axios'
    import { connect } from 'react-redux'
    import { changeBannersAction, changeRecommendsAction } from '../store/actionCreators'
    
    export class Category extends PureComponent {
    
      componentDidMount() {
        // 发送网络请求, 获取到banners和recommends数据
        axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
          const banners = res.data.data.banner.list
          const recommends = res.data.data.recommend.list
          console.log(banners, recommends)
    
          // 调用映射过来的方法, 修改banners和recommends 
          this.props.changeBanners(banners)
          this.props.changeRecommends(recommends)
        })
      }
    
      render() {
        return (
          <div>Category</div>
        )
      }
    }
    
    // 映射方法用于修改store中的banners和recommends
    const mapDispatchToProps = (dispatch) => ({
      changeBanners(banners) {
        dispatch(changeBannersAction(banners))
      },
      changeRecommends(recommends) {
        dispatch(changeRecommendsAction(recommends))
      }
    })
    
    export default connect(null, mapDispatchToProps)(Category)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    目前, store中存放的就是网络请求获取到的数据, 接下来就在About页面进行展示

    import React, { PureComponent } from 'react'
    import { connect } from 'react-redux'
    
    export class About extends PureComponent {
    
      render() {
        // 在props中获取到映射过来的数据
        const { banners, recommends } = this.props
    
        return (
          <div>
            <h2>轮播图展示</h2>
            <ul>
              {
                banners.map(item => {
                  return <li key={item.acm}>{item.title}</li>
                })
              }
            </ul>
    
            <h2>推荐数据展示</h2>
            <ul>
              {
                recommends.map(item => {
                  return <li key={item.acm}>{item.title}</li>
                })
              }
            </ul>
          </div>
        )
      }
    }
    
    const mapStateToProps = (state) => ({
      banners: state.banners,
      recommends: state.recommends
    })
    
    // 表示将数据映射到About组件中
    export default connect(mapStateToProps)(About)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    redux中进行异步操作

    上面的代码有一个缺陷:

    我们必须将网络请求的异步代码放到组件的生命周期中来完成;

    事实上,网络请求到的数据也属于我们状态管理的一部分,更好的一种方式应该是将其也交给redux来管理;

    在这里插入图片描述

    但是在redux中如何可以进行异步的操作呢?

    答案就是使用中间件(Middleware), 如果学习过Express或Koa框架的小伙伴对中间件的概念一定不陌生;

    由于在正常情况下, store.dispatch()只能派发一个对象, 不能派发函数; 如果dispatch想要派发函数, 我们必须要使用中间件对该store进行增强

    使用中间件, 在redux中发送网络请求

    首先安装redux-thunk库, 引入中间件

    安装redux-thunk库: npm i redux-thunk, 在该库中有一个中间件thunk, 如下方式应用thunk中间件

    import { createStore, applyMiddleware } from "redux";
    import reducer from "./reducer";
    // 导入中间件
    import thunk from "redux-thunk";
    
    // 应用中间件
    const store = createStore(reducer, applyMiddleware(thunk))
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    应用之后, store.dispatch()就可以派发函数了

    // 定义一个返回函数的action
    export const fetchHomeMultidataAction = () => {
      function foo() {
        console.log("aaa")
      }
    
      return foo
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 派发action
    const mapDispatchToProps = (dispatch) => ({
      fetchHomeMultidata() {
        // 派发一个函数, 内部返回的函数自动执行
        dispatch(fetchHomeMultidataAction())
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    自动执行action中的返回的函数时, 会传给这个函数一个dispatch函数和getState函数;

    dispatch函数: 用于我们之后再次派发action;

    getState函数: 考虑到我们之后的一些操作需要依赖原来的状态,调用getState函数可以让我们可以获取之前的一些状态;

    我们就可以在返回的该函数中, 编写异步的网络请求相关代码

    import axios from "axios"
    
    export const changeBannersAction = (banners) => ({
      type: CHANGE_BANNERS,
      banners
    })
    export const changeRecommendsAction = (recommends) => ({
      type: CHANGE_RECOMMENDS,
      recommends
    })
    
    export const fetchHomeMultidataAction = () => {
      // 派发时返回的该函数自动执行, 且传入两个参数dispatch, getState
      return (dispatch, getState) => {
        axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
          const banners = res.data.data.banner.list
          const recommends = res.data.data.recommend.list
    
          // 获取到数据后在派发action
          dispatch(changeBannersAction(banners))
          dispatch(changeRecommendsAction(recommends))
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    Leetcode---370周赛
    一周拿下多线程+源码+微服务+分布式+调优,金九银十轻松跳槽,Offer拿到手软
    HTML+CSS+JS静态网页设计【篮球NBA介绍体育运动】web前端学生作业源码
    PDFgear——一款接入AI智能化模型的免费PDF聊天软件
    【Docker】APISIX Dashboard 容器化部署
    云计算 2月28号 (linux的磁盘分区)
    java.lang.NoClassDefFoundError: ch/qos/logback/core/util/StatusPrinter2
    Rust 中使用 :: 这种语法的几种情况
    2023-11-16 android 编译提示module freg.default missing dependencies:
    Metasploit——渗透攻击模块(Exploit)
  • 原文地址:https://blog.csdn.net/m0_71485750/article/details/126754159