• Redux使用详解(二)--react-redux的使用


    Redux

    react-redux

    上redux官方帮助我们提供了 react-redux 的库
    npm install react-redux
    或者yarn add react-redux

    组件中异步操作

    在之前简单的案例中,redux中保存的counter是一个本地定义的数据
    我们可以直接通过同步的操作来dispatch action,state就会被立即更新。
    但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。
    在之前学习网络请求的时候我们讲过,网络请求可以在class组件的componentDidMount中发送,所以我们可以有这样的结构:

    实现在Home组件中请求banners和recommends的数据
    在Profile组件中展示banners和recommends的数据

    actionCreators.js

    import * as actionTypes from "./constants"
    
    export const addNumberAction = (num) => ({
      type: actionTypes.ADD_NUMBER,
      num
    })
    
    export const subNumberAction = (num) => ({
      type: actionTypes.SUB_NUMBER,
      num
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    constants.js

    export const ADD_NUMBER = "add_number"
    export const SUB_NUMBER = "sub_number"
    
    • 1
    • 2

    index.js

    import { createStore } from "redux"
    import reducer from "./reducer"
    
    const store = createStore(reducer)
    
    export default store
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    reducer.js

    import * as actionTypes from "./constants"
    
    const initialState = {
      counter: 100
    }
    
    function reducer(state = initialState, action) {
      switch (action.type) {
        case actionTypes.ADD_NUMBER:
          return { ...state, counter: state.counter + action.num }
        case actionTypes.SUB_NUMBER:
          return { ...state, counter: state.counter - action.num }
        default:
          return state
      }
    }
    
    export default reducer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    home.jsx

    import React, { PureComponent } from 'react'
    import store from "../store"
    import { addNumberAction } from '../store/actionCreators'
    
    export class Home extends PureComponent {
      constructor() {
        super()
    
        this.state = {
          counter: store.getState().counter
        }
      }
    
      componentDidMount() {
        store.subscribe(() => {
          const state = store.getState()
          this.setState({ counter: state.counter })
        })
      }
    
      addNumber(num) {
        store.dispatch(addNumberAction(num))
      }
    
      render() {
        const { counter } = this.state
    
        return (
          <div>
            <h2>Home Counter: {counter}</h2>
            <div>
              <button onClick={e => this.addNumber(1)}>+1</button>
              <button onClick={e => this.addNumber(5)}>+5</button>
              <button onClick={e => this.addNumber(8)}>+8</button>
            </div>
          </div>
        )
      }
    }
    
    export default Home
    
    • 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
    • 41

    profile.jsx

    import React, { PureComponent } from 'react'
    import store from "../store"
    import { subNumberAction } from '../store/actionCreators'
    
    export class Profile extends PureComponent {
      constructor() {
        super()
    
        this.state = {
          counter: store.getState().counter
        }
      }
    
      componentDidMount() {
        store.subscribe(() => {
          const state = store.getState()
          this.setState({ counter: state.counter })
        })
      }
    
      subNumber(num) {
        store.dispatch(subNumberAction(num))
      }
    
      render() {
        const { counter } = this.state
    
        return (
          <div>
            <h2>Profile Counter: {counter}</h2>
            <div>
              <button onClick={e => this.subNumber(1)}>-1</button>
              <button onClick={e => this.subNumber(5)}>-5</button>
              <button onClick={e => this.subNumber(8)}>-8</button>
              <button onClick={e => this.subNumber(20)}>-20</button>
              <button onClick={e => this.subNumber(100)}>-100</button>
            </div>
          </div>
        )
      }
    }
    
    export default Profile
    
    • 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
    • 41
    • 42
    • 43

    App.jsx

    import React, { PureComponent } from 'react'
    import Home from './pages/home'
    import Profile from './pages/profile'
    import "./style.css"
    import store from "./store"
    
    export class App extends PureComponent {
      constructor() {
        super()
    
        this.state = {
          counter: store.getState().counter
        }
      }
    
      componentDidMount() {
        store.subscribe(() => {
          const state = store.getState()
          this.setState({ counter: state.counter })
        })
      }
    
      render() {
        const { counter } = this.state
    
        return (
          <div>
            <h2>App Counter: {counter}</h2>
    
            <div className='pages'>
              <Home/>
              <Profile/>
            </div>
          </div>
        )
      }
    }
    
    export default App
    
    • 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

    redux中异步操作

    上面的代码有一个缺陷:
    我们必须将网络请求的异步代码放到组件的生命周期中来完成;
    事实上,网络请求到的数据也属于我们状态管理的一部分,更好的一种方式应该是将其也交给redux来管理;

    但是在redux中如何可以进行异步的操作呢?
    答案就是使用中间件(Middleware);
    如果学习过Express或Koa框架那么对中间件的概念一定不陌生;
    在这类框架中,Middleware可以帮助我们在请求和响应之间嵌入一些操作的代码,比如cookie解析、日志记录、文件压缩等操作;

    理解中间件

    redux也引入了中间件(Middleware)的概念:
    这个中间件的目的是在dispatch的action和最终达到的reducer之间,扩展一些自己的代码;
    比如日志记录、调用异步接口、添加代码调试功能等等;
    我们现在要做的事情就是发送异步的网络请求,所以我们可以添加对应的中间件:
    这里官网推荐的、包括演示的网络请求的中间件是使用 redux-thunk;

    redux-thunk是如何让我们可以发送异步请求的呢
    我们知道 默认情况下的dispatch(action) action需要是一个javascript对象
    redux-thunk可以让dispatch(action函数) action可以是一个函数
    该函数会被调用 并且会传给这个函数一个dispatch函数和getState函数
    dispatch函数用于我们之后再次派发action
    getState函数考虑到我们之后的一些操作需要依赖原来的状态 用于让我们可以获取之前的一些状态

    如何使用redux-thunk

    1.安装redux-thunk
    yarn add redux-thunk 或 npm install redux-thunk
    2.在创建store时传入应用了middleware的enhance函数
    通过applyMiddleware来结合多个Middleware, 返回一个enhancer;
    将enhancer作为第二个参数传入到createStore中;

    const enhancer = applyMiddleware(thunkMiddlewatr)
    const store = createStore(reducer, enthancer)
    
    • 1
    • 2

    3.定义返回一个函数的action:
    注意:这里不是返回一个对象了,而是一个函数;
    该函数在dispatch之后会被执行;

    const getHomeMultidataAction = () =>{
    	return (dispatch) => {
    		axios.get('url').then(res => {
    			const data = res.data.data
    			dispatch(changeBannersAction(data.banner.list))
    		})
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    redux-devtools

    我们之前讲过,redux可以方便的让我们对状态进行跟踪和调试,那么如何做到呢?
    redux官网为我们提供了redux-devtools的工具;
    利用这个工具,我们可以知道每次状态是如何被修改的,修改前后的状态变化等等;

    安装该工具需要两步:
    第一步:在对应的浏览器中安装相关的插件(比如Chrome浏览器扩展商店中搜索Redux DevTools即可);
    第二步:在redux中继承devtools的中间件;

    import { createStore, applyMiddleware, compose } from "redux"
    import thunk from "redux-thunk"
    import reducer from "./reducer"
    
    // 正常情况下 store.dispatch(object)
    // 想要派发函数 store.dispatch(function)
    
    // redux-devtools
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
    const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
    
    export default store
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    combineReducers函数

    目前我们合并的方式是通过每次调用reducer函数自己来返回一个新的对象。
    事实上,redux给我们提供了一个combineReducers函数可以方便的让我们对多个reducer进行合并:
    那么combineReducers是如何实现的呢?
    事实上,它也是将我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);
    在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state;
    新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;

    import { createStore, applyMiddleware, compose, combineReducers } from "redux"
    import thunk from "redux-thunk"
    
    import counterReducer from "./counter"
    import homeReducer from "./home"
    import userReducer from "./user"
    
    // 将两个reducer合并在一起
    const reducer = combineReducers({
      counter: counterReducer,
      home: homeReducer,
      user: userReducer
    })
    
    // redux-devtools
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
    const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    界面组件DevExpress WinForms v22.1新版亮点 - 支持High-DPI
    痞子衡嵌入式:浅谈i.MXRT1xxx系列MCU时钟相关功能引脚的作用
    读书笔记:从缺陷中学习C++
    网课答案公众号制作教程
    索引失效的几种情况
    Linux 是如何进行内存分配的
    c++基础2
    产品运营的场景和运营策略
    Tomcat运行常见问题
    MyBatis-Plus 实现多租户管理的实践
  • 原文地址:https://blog.csdn.net/weixin_65402230/article/details/128209032