• 【React】第十部分 redux


    【React】第十部分 redux和react-redux



    10. redux

    学习文档

    1. 英文文档
    2. 中文文档
    3. Github

    10.1 什么是redux?

    1. redux是一个专门用于做状态管理的JS库(不是react插件库,不是官方的插件)。

    2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用

    3. 作用: 集中式管理react应用中多个组件共享的状态

    10.2 redux的工作流程图

    在这里插入图片描述

    10.3 redux三个核心概念

    10.3.1 action

    1. action : 动作的对象

    2. 有两个属性

      • type : 标识属性, 值为字符串, 唯一, 必不可少
      • data : 数据属性,值类型任意,可选
    3. 举个例子:{type:'add',data:3}

    10.3.2 reducer

    1. 用于初始化状态、加工状态

    2. 加工时,根据旧的state和action, 产生新的state的纯函数

    10.3.3 store(核心)

    1. 将state、action、reducer联系在一起的对象

    10.4 redux基本使用(完整写法)

    // 完整的写法
    1. npm install --save redux 安装redux
    
    2. 在src下创建
    			-- redux
    				-- store.js
    				-- xxx_reducer.js
    				-- xxx_action.js
    
    
    3. 在store.js
    		- 通过去引入redux中的createStore(),创建一个store
    	 	- createStore()在创建的时候需要传入一个指定服务的reducer
    		- 最后暴露store
    示例:
    	// legacy_createStore是用于创建redux中最为核心的store
    	import {legacy_createStore as createStore} from "redux"
    	// 导入为xxx组件服务的reducer
    	import xxxReducer from "./xxx_reducer"
    	// 暴露store
    	export default createStore(xxxReducer)
    
    
    4.在xxx_reducer.js
    	- reducer本质是一个函数,因为上述的操作导致它可以接收到两个参数previousState(之前的状态)和action(动作对象),在这里做一些操作然后返回加工后的状态
    	- reducer有两个作用:初始化状态和加工状态
      - reducer第一次调用的时候,是store自动触发的,传入的previousState为undefined
    
    示例:
    let initState = 0
    export default function countReducer(previousState = initState,action){
        // 在action对象中可以获取两个参数,type和data
        const {type,data} = action
        switch (type) {
            case 'plus':
                return previousState + data*1
            case 'subtract':
                return previousState - data*1
            default:
                // 初始化
                return previousState
        }
    }
    
    
    5. 在xxx_action
    	这个文件是专门为xxx组件生成action对象
    	示例:
    	export const plusAction = data => ({type:'plus',data})
    	export const subtractAction = data => ({type:'subtract',data})
      
      
    6. 创建一个文件constant.js,该文件是用来去定义action对象中type类型的常量,目的是为了避免程序员写错,便于管理
    export const PLUS = 'plus'
    export const SUBSCRIBE = 'subscribe'
      
      
    // 上述工作完成后,redux文件就写好了
    // 下面讲述在组件中怎么使用
    7. 这里我创建一个count组件
    下面示例中用到几个API,解释一下它们的作用
    `store.getState()` 可以获取加工完后的状态
    `store.dispatch(action)` 派发一个action给store,store在交给reducer加工
    `store.subscribe(()=>{})` 可以监视redux状态的变化,如果发生变化就调用该函数
    
    示例:
    import React, { Component } from 'react'
    // 引入store
    import store from "../../redux/store"
    // 引入action
    import {plusAction,subtractAction} from "../../redux/count_action"
    export default class Count extends Component {
        render() {
            return (
                <div>
                    {/* 调用store上的getState()获取可以获取加工完后的状态 */}
                    <h1>当前的值为: {store.getState()}</h1>
                    <select ref={c=>this.selectVal = c}>
                        <option value="1"> 1 </option>
                        <option value="2"> 2 </option>
                        <option value="3"> 3 </option>
                        <option value="4"> 4 </option>
                    </select>
                    <button onClick={this.plus}> + </button> &nbsp;
                    <button onClick={this.subtract}> - </button> &nbsp;
                    <button onClick={this.oddPlus}> 奇数+ </button> &nbsp;
                    <button onClick={this.delayPlus}> 延迟+ </button> &nbsp;
                </div>
            )
        }
    
        componentDidMount(){
            // subscribe去监视redux的状态是否发生变化,只要一发生变化就调用render()
            store.subscribe(()=>{
                // 可以利用setState,更新完状态后调用render()
                this.setState({})
            })
        }
    
        plus = () => {    
            // 去派发一个action
            store.dispatch(plusAction(this.selectVal.value*1))      
        }
        subtract = () => {
            // 去派发一个action
            store.dispatch(subtractAction(this.selectVal.value*1))
        }
        oddPlus = () => {
            if(store.getState() % 2 !== 0){
                // 去派发一个action
                store.dispatch(plusAction(this.selectVal.value*1))
            }
        }
        delayPlus = () => {
            setTimeout(() => {
                store.dispatch(plusAction(this.selectVal.value*1))
            }, 500);
        }
    }
    
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    10.5 异步action

    什么时候使用异步action呢?

    当想要对状态进行修改的时候,但是具体的数据是靠异步任务返回的

    同步action : action是一个对象

    异步action : action是一个函数

    import store from "./store"
    // 返回的是一个对象,所以它是同步的action
    export const subtractAction = data => ({type:SUBSCRIBE,data})
    // 返回的是一个函数,并且用到了异步任务,所以它是异步的action
    // 返回的函数由store帮忙调用
    export const addAsyncAction = (data,time) =>{
        return ()=>{
            setTimeout(() => {
                store.dispatch(plusAction(data))
            }, time);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    想要使用异步action,需要借助中间件

    npm install redux-thunk
    
    • 1
    在store.js文件下
    // legacy_createStore是用于创建redux中最为核心的store
    // applyMiddleware用来支持使用中间件
    import {legacy_createStore as createStore,applyMiddleware} from "redux"
    // 导入为Count服务的reducer
    import countReducer from "./count_reducer"
    // 引入redux-thunk,用来支持使用异步action
    import thunk from "redux-thunk"
    // 暴露store
    // 第一个参数用来接收reducer,第二个参数用来支持使用中间件
    export default createStore(countReducer,applyMiddleware(thunk))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    10.6 react-redux

    10.6.1 react-redux的理解

    要和redux区分开来, react-redux 一个react插件库

    作用: 专门用来简化react应用中使用redux

    10.6.2 react-redux将所有组件分成两大类

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMXyZjBA-1660203633424)(C:\Users\hp\Desktop\Note\图片\react-redux.png)]

    这里需要明确三个点:

    1. UI组件:不能使用任何的redux的api,它只负责页面的呈现、交互等
    2. 容器组件:负责和redux通信,将结果交给UI组件,UI组件通过props接收

    10.6.3 react-redux 的基本使用

    1. npm install react-redux 安装react-redux
    
    2. 在src下创建一个containers文件,用于存放容器组件
    
    3. 引入react-redux中connect这个方法,创建一个容器组件并且和UI组件建立联系
    	connect(mapStateToProps,mapDispatchToProps)(UI组件)
    	- mapStateToProps(state):映射状态,返回的是一个对象
    	- mapDispatchToProps(dispatch):映射的是操作对象的方法,返回的是一个对象
    
    示例:UI组件和容器组件写一起,可以减少创建多个文件
    import React, { Component } from 'react'
    // 引入react-rudex中的connect
    import {connect} from "react-redux"
    // 引入action
    import {plusAction} from "../../redux/count_action"
    // 引入store
    import store from "../../redux/store"
    // UI组件
    class Count extends Component {
        render() {
            return (
                <div>
                    {/* 调用store上的getState()获取修改后的值 */}
                    <h1>当前的值为: {this.props.count}</h1>
                    <select ref={c=>this.selectVal = c}>
                        <option value="1"> 1 </option>
                        <option value="2"> 2 </option>
                        <option value="3"> 3 </option>
                        <option value="4"> 4 </option>
                    </select>
                    <button onClick={this.plus}> + </button> &nbsp;
                </div>
            )
        }
        
        plus = () => {    
            this.props.plus(this.selectVal.value) 
        }
    }
    
    
    const mapStateToProps = (state) =>{
       	/* 
            可以拿到总的state
            mapStateToProps函数返回的是一个对象:
            返回对象中key就作为传递UI组件props的key
            返回对象中的value就作为传递UI组件props的value
            作用:用于给UI组件传递状态
         */
        return {count:state}
    }
    const mapDispatchToProps = (dispatch) =>{
      	/* 
           	mapDispatchToProps函数返回的是一个对象
            返回对象中key就作为传递UI组件props的key
            返回对象中的value就作为传递UI组件props的value
            作用:用于给UI组件传递操作状态的方法
         */
        return {
            plus:(data)=>{dispatch(plusAction(data))}
        }
    }
    
    // 创建容器组件并且暴露
    export default connect(mapStateToProps,mapDispatchToProps)(Count)
    
    
    4.使用容器组件需要传入store,用于和redux建立联系
    <容器组件 store={store}/>
    
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    当使用了connect方法后,它具备了监视redux的能力,所以不需要再写store.subscribe()去监视redux状态的变化

    下面代码就可以不用再写了

        componentDidMount(){ 
          // subscribe去监视redux的状态是否发生变化,只要一发生变化就调用render()
            store.subscribe(()=>{
               // 可以利用setState,更新完状态后调用render()
                this.setState({})
            })
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    10.6.4 优化 简写mapDispatchToProps

    // 创建容器组件并且暴露
    export default connect(
        state=>({count:state}),
        /* 
            mapDispatchToProps也可以写成一个对象,
            这个对象中的value只要传入对应的action,
            当UI去调用时实际上是调用action的方法,返回一个{type:xx,data:xx}
            那么react-redux就会自动帮我们去派发action
         */
        {
            plus:plusAction
        }
        )(Count)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    10.6.5 provider组件的使用

    <容器组件 store={store}/>
    <容器组件 store={store}/>
    <容器组件 store={store}/>
    
    • 1
    • 2
    • 3

    例如上述代码:如果容器组件很多那么需要传递很多store

    provider组件的作用:减少容器组件中传递store

    使用方法

    在index.js中
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    import {Provider} from "react-redux"
    import store from './redux/store';
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        {/* 在这里使用,表示App下的所有容器组件都有store,也就是和redux和容器组件建立联系 */}
        <Provider store={store}>
          <App />
        </Provider>
      </React.StrictMode>
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    10.6.6 实现数据共享

    只需要在store.js文件下再去引入combineReducers

    // 引入legacy_createStore用来创建store
    // 引入applyMiddleware用来支持使用中间件
    // 引入combineReducers用来实现数据共享
    import {legacy_createStore,applyMiddleware,combineReducers} from "redux"
    // 引入支持异步action的中间件
    import thunk from "redux-thunk"
    // 引入reducer
    import count_reducer from "./reducers/count_reducer"
    import person_reducer from "./reducers/person_reducer"
    // 切记:combineReducers传入的对象 === redux中保存的总状态对象 
    export default legacy_createStore(combineReducers({count_reducer,person_reducer}),applyMiddleware(thunk))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在读取状态的时候需要注意:

    // 创建容器组件和UI组件建立联系,并且暴露
    export default connect(
      	// 切记:combineReducers传入的对象 === redux中保存的总状态对象 
      	// state.xxx  
        (state)=>({calculate:state.count_reducer,personNum:state.person_reducer}),
        {
            plus:plusAction
        }
    )(Count)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述


    总结

    以上就是今天要讲的内容,希望对大家有所帮助!!!

  • 相关阅读:
    MYSQL之主从复制
    YOLOv5算法改进(21)— 添加CA注意力机制 + 更换Neck网络之BiFPN + 更换损失函数之EIoU
    ES6基础知识
    Java并发编程常见面试题
    OpenDRIVE地图第二篇:车道LANE
    java毕业设计人才招聘网源码+lw文档+mybatis+系统+mysql数据库+调试
    Linux 工作使用场景
    Android 12(S) 图像显示系统 - BufferQueue的工作流程(十一)
    RabbitMQ远程访问
    JAVA练习题36:打乱一维数组中的数据,并按照4个一组的方式添加到二维数组中
  • 原文地址:https://blog.csdn.net/Trees__/article/details/126286672