• React - redux 使用(由浅入深)


    中文文档:http://www.redux.org.cn/
    英文文档:https://redux.js.org/
    Github:https://github.com/reactjs/redux
    可直接参照 目录十 进行使用 react-redux

    一. redux理解

    1. redux 介绍

    • redux 是一个专门用于做状态管理的JS库(不是react插件库)。
    • 它可以用在 react, angular, vue 等项目中, 但基本与 react 配合使用。
    • 作用: 集中式管理 react 应用中多个组件共享的状态。
    • 可以构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。
    • 体小精悍(只有2kB,包括依赖),却有很强大的插件扩展生态。

    2. redux 使用情况

    • 一个状态多个组件使用,多个组件可以随时拿到(共享)。
    • 一个组件需要改变另一个组件的状态(通信)。
    • 能不使用就不使用, 状态共享使用不方便时考虑使用。

    3. redux 工作流程

    在这里插入图片描述

    4. redux 三个核心概念

    4.1 Action

    • 可以省略创建。
    • 把数据从应用传到 store 的有效载荷,Action 是 store 数据的唯一来源。
    • 一般来说会通过 store.dispatch() 将 action 传到 store。
    • 有两个属性
      type :标识属性, 值为字符串, 唯一, 必要属性。
      data :数据属性, 值类型任意, 可选属性

    4.2 Store

    • 用来维持应用所有的 state 树 的一个对象,改变 store 内 state 的惟一途径是对它 dispatch 一个 action。
    • Store 不是类,它只是有几个方法的对象。 要创建它,只需要把根部的 reducing 函数传递给 createStore。

    4.3 Reducers

    • 指定了应用状态的变化如何响应 actions 并发送到 store 。
    • Reduce r函数会接收到两个参数,分别为:之前的状态动作对象
    • Reducer 有两个作用:初始化状态、加工状态。
    • Reducer 的第一次调用时,是store自动触发的,传递的 preState(之前的状态) 是undefined

    5. redux 核心API

    5.1 createStore()

    • 创建一个 Redux store 来以存放应用中所有的 state。
    • 应用中应有且仅有一个 store。

    5.2 Store

    • 用来维持应用所有的 state 树 的一个对象。 改变 store 内 state 的惟一途径是对它 dispatch 一个 action。
    • Store 不是类。它只是有几个方法的对象。 要创建它,只需要把根部的 reducing 函数传递给 createStore。
    5.2.1 Store 方法
    5.2.1.1 getState()

    store.getState()

    • 返回应用当前的 state 树。
    • 它与 store 的最后一个 reducer 返回值相同。
    5.2.1.2 dispatch(action)

    store.dispatch(action)

    • 分发 action。这是触发 state 变化的惟一途径。
    • 会使用当前 getState() 的结果和传入的 action 以同步方式的调用 store 的 reduce 函数。返回值会被作为下一个 state。
    5.2.1.3 subscribe(listener)

    store.subscribe(() => { });

    • 添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。可以在回调函数里调用 getState() 来拿到当前 state。
    • 状态改变后重新渲染,有两种方法:
      (1)在组件的 componentDidMount 生命周期钩子中调用 store.subscribe
      componentDidMount() {
          // 监听redux中状态的变化,只要变化,就调用render
          store.subscribe(() => {
          	//状态假更新,每次状态更新都会调用render
              this.setState({});
          });
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      (2)在 index.js 文件中检测 store 中状态的改变,一旦发生改变重新渲染
      import React from "react";
      import reactDOM from "react-dom";
      import App from "./App";
      import store from "./redux/store";
      reactDOM.render(<App />, document.getElementById("root"));
      // store中状态改变,重新渲染dom
      store.subscribe(() => {
        reactDOM.render(<App />, document.getElementById("root"));
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    二. redux精简版使用(不使用 Action)

    1. src 文件夹下创建一个 redux 文件夹,并在 redux 文件夹中创建两个文件,分别是:store.jscount_reducer.js,对应store,reducer
    2. store.js中修改

      1.引入redux中的createState函数,创建一个store
      2. createState调用时,要传入一个为其服务的reducer
      3. 记得暴露 store

      /**
       * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
       */
      // 引入createStore,专门用于创建redux中最为核心的store对象
      import { createStore } from "redux";
      // 引入为count组件服务的reducer
      import countReducer from "./count_reducer";
      
      const store = createStore(countReducer);
      export default store;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    3. count_reducer.js中修改
      /**
       * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数
       * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action)
       */
      const intState = 0; //初始化状态
      // preState===undefined时,preState = intState
      export default function countReducer(preState = intState, action) {
        // 从action对象中获取:type,data
        const { type, data } = action;
        // 根据type决定加工数据
        switch (type) {
          // 加
          case "increment":
            return preState + data;
          //  减
          case "decrement":
            return preState - data;
          default:
            return preState;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    4. 组件中使用
      1. 获取状态:store.getState()
      2. 改变状态:store.dispatch({ type: “方法类型”, data: 需要操作的数据 })
      3. 状态改变后重新渲染
      import React, { Component } from "react";
      // 引入store,用于获取redux中保存的状态
      import store from "../../redux/store";
      
      export default class Count extends Component {
        /**
         * 在index.js中监听状态改变进行渲染,一劳永逸
         */
        // componentDidMount() {
        //   // 监听redux中状态的变化,只要变化,就调用render
        //   store.subscribe(() => {
        //     // 假更新
        //     this.setState({});
        //   });
        // }
        //   加法
        increment = () => {
          const { value } = this.selectNumber;
          store.dispatch({ type: "increment", data: value * 1 });
        };
        //   减法
        decrement = () => {
          const { value } = this.selectNumber;
          store.dispatch({ type: "decrement", data: value * 1 });
        };
        //   奇数再加
        incrementIfOdd = () => {
          const { value } = this.selectNumber;
          const count = store.getState();
          if (count % 2 !== 0) {
            store.dispatch({ type: "increment", data: value * 1 });
          }
        };
        //   异步加
        incrementAsync = () => {
          const { value } = this.selectNumber;
          setTimeout(() => {
            store.dispatch({ type: "increment", data: value * 1 });
          }, 500);
        };
        render() {
          return (
            <div>
              <h1>当前求和为:{store.getState()}</h1>
              <select ref={(cur) => (this.selectNumber = cur)}>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
              </select>
              &nbsp;&nbsp;
              <button onClick={this.increment}>+</button>
              &nbsp;&nbsp;
              <button onClick={this.decrement}>-</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementAsync}>异步加</button>
            </div>
          );
        }
      }
      
      • 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

    三. redux完整版使用(在 redux精简版基础上使用 Action)

    1. 在redux文件夹中新增两个文件,分别是:constant.jscount__action.js
      constant.js 放置容易写错的type值
      count__action.js 专门用于创建action对象
    2. constant.js 文件修改
      /**
       * 该模块用于定义action对象中type类型的常量值
       * 目的只有一个:便于管理的同时防止程序员单词写错
       */
      export const INCREMENT = "increment";
      export const DECREMENT = "decrement";
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. count__action.js 文件修改
      /**
       * 该文件专门为Count组件生成action对象
       */
      import { INCREMENT, DECREMENT } from "./constant";
      
      // 正常方式
      // export const createIncrementAction = (data) => {
      //   return { type: "increment", data };
      // };
      // 简写方式
      export const createIncrementAction = (data) => ({ type: INCREMENT, data });
      export const createDecrementAction = (data) => ({ type: DECREMENT, data });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    4. count_reducer.js文件修改,使用 constant.js 文件
      /**
       * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数
       * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action)
       */
      import { INCREMENT, DECREMENT } from "./constant";
      
      const intState = 0; //初始化状态
      export default function countReducer(preState = intState, action) {
        // 从action对象中获取:type,data
        const { type, data } = action;
        // 根据type决定加工数据
        switch (type) {
          // 加
          case INCREMENT:
            return preState + data;
          //  减
          case DECREMENT:
            return preState - data;
          default:
            return preState;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    5. 使用的组件中修改
      import React, { Component } from "react";
      // 引入store,用于获取redux中保存的状态
      import store from "../../redux/store";
      // 引入actionCreator,专门用于创建action对象
      import {
        createIncrementAction,
        createDecrementAction,
      } from "../../redux/count_action";
      
      export default class Count extends Component {
        //   加法
        increment = () => {
          const { value } = this.selectNumber;
          store.dispatch(createIncrementAction(value * 1));
        };
        //   减法
        decrement = () => {
          const { value } = this.selectNumber;
          store.dispatch(createDecrementAction(value * 1));
        };
        //   奇数再加
        incrementIfOdd = () => {
          const { value } = this.selectNumber;
          const count = store.getState();
          if (count % 2 !== 0) {
            store.dispatch(createIncrementAction(value * 1));
          }
        };
        //   异步加
        incrementAsync = () => {
          const { value } = this.selectNumber;
          setTimeout(() => {
            store.dispatch(createIncrementAction(value * 1));
          }, 500);
        };
        render() {
          return (
            <div>
              <h1>当前求和为:{store.getState()}</h1>
              <select ref={(cur) => (this.selectNumber = cur)}>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
              </select>
              &nbsp;&nbsp;
              <button onClick={this.increment}>+</button>
              &nbsp;&nbsp;
              <button onClick={this.decrement}>-</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementAsync}>异步加</button>
            </div>
          );
        }
      }
      
      • 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

    四. redux 异步action(在 redux完整版基础上使用 异步action)

    1. 安装 redux-thunk
      yarn add redux-thunk
    2. store.js 中修改配置redux-thunk
      /**
       * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
       */
      
      // 引入createStore,专门用于创建redux中最为核心的store对象
      import { createStore, applyMiddleware } from "redux";
      // 引入为count组件服务的reducer
      import countReducer from "./count_reducer";
      // 引入redux-thunk,用于支持异步action
      import thunk from "redux-thunk";
      
      const store = createStore(countReducer, applyMiddleware(thunk));
      export default store;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    3. count_action.js 文件中配置 异步action
      /**
       * 该文件专门为Count组件生成action对象
       */
      import { INCREMENT, DECREMENT } from "./constant";
      /**
       * 同步action,就是指action的值为Object类型的一般对象
       */
      // 正常方式
      // export const createIncrementAction = (data) => {
      //   return { type: "increment", data };
      // };
      // 简写方式
      export const createIncrementAction = (data) => ({ type: INCREMENT, data });
      export const createDecrementAction = (data) => ({ type: DECREMENT, data });
      /**
       * 异步action,就是指action的值是函数,异步action中一般都会调用同步action,异步action不是必须要用的
       */
      export const createIncrementAsyncAction = (data, time) => {
        return (dispatch) => {
          setTimeout(() => {
            // 异步任务有结果后,分发一个同步的action去真正操作数据
            dispatch(createIncrementAction(data));
          }, time);
        };
      };
      
      • 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
    4. 组件中修改使用 异步action
      // 引入actionCreator,专门用于创建action对象
      import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction,
      } from "../../redux/count_action";
      
      //   异步加
      incrementAsync = () => {
        const { value } = this.selectNumber;
        // setTimeout(() => {
        store.dispatch(createIncrementAsyncAction(value * 1, 500));
        // }, 500);
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

    五. react-redux 的基本使用(在 redux 异步action修改使用 react-redux)

    1. 安装 react-redux
      yarn add react-redux
    2. 创建一个容器组件

      1.在 src 文件夹下创建一个 containers 容器文件夹
      2.containers 文件夹中创建一个 Count 文件夹
      3.Count 文件夹下创建一个index.jsx文件

      // 引入Count的UI组件
      import CountUI from "../../components/Count";
      // 引入connect用于连接UI组件与redux
      import { connect } from "react-redux";
      // 引入action
      import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction,
      } from "../../redux/count_action";
      
      /**
       * 1. mapStateToProps 函数的返回值是一个对象
       * 2. 返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件的props的value
       * 3. mapStateToProps 函数用于传递状态
       */
      function mapStateToProps(state) {
        return {
          count: state,
        };
      }
      /**
       * 1. mapDispatchToProps 函数的返回值是一个对象
       * 2. 返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件的props的value
       * 3. mapDispatchToProps 函数用于传递操作状态的方法
       */
      function mapDispatchToProps(dispatch) {
        return {
          add: (data) => {
            // 通知redux执行方法
            dispatch(createIncrementAction(data));
          },
          delete: (data) => dispatch(createDecrementAction(data)),
          addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)),
        };
      }
      // 使用connect()()创建并暴露一个Count的容器组件
      export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
      
      • 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
    3. App.js 文件中修改引入的容器组件地址,并传入 store
      import React, { Component } from "react";
      import Count from "./containers/Count";
      import store from "./redux/store";
      
      export default class App extends Component {
        render() {
          return (
            <div>
              {/* 给容器组件传递store */}
              <Count store={store} />
            </div>
          );
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    4. 修改组件中获取状态和修改状态的方式,通过this.props方式
      • 获取状态:
        this.props.count
      • 调用 action 修改状态,对应mapDispatchToProps中的方法
        this.props.add(value * 1);

    六. react-redux 的优化使用(在 react-redux 的基本使用 中修改)

    1. 容器组件 和 UI组件 合并成一个组件
      connect模板:

      export default connect(
         (state) => ({ key: value }),//映射状态
         { key: action方法, }//映射操作状态的方法
      )(UI组件);
      
      • 1
      • 2
      • 3
      • 4

      1.将 src/component/Count/index.jsx 中的内容 剪切放入 src/container/Count/index.jsx
      2.并删除 src/component 中的Count组件
      3.优化connect()()写法,简化mapDispatchToProps写法

      import React, { Component } from "react";
      // 引入connect用于连接UI组件与redux
      import { connect } from "react-redux";
      // 引入action
      import {
        createIncrementAction,
        createDecrementAction,
        createIncrementAsyncAction,
      } from "../../redux/count_action";
      
      class Count extends Component {
        //   加法
        increment = () => {
          const { value } = this.selectNumber;
          this.props.add(value * 1);
        };
        //   减法
        decrement = () => {
          const { value } = this.selectNumber;
          this.props.delete(value * 1);
        };
        //   奇数再加
        incrementIfOdd = () => {
          const { value } = this.selectNumber;
          if (this.props.count % 2 !== 0) {
            this.props.add(value * 1);
          }
        };
        //   异步加
        incrementAsync = () => {
          const { value } = this.selectNumber;
          this.props.addAsync(value * 1,500);
        };
        render() {
          console.log("UI组件接收到的props值:", this.props);
          return (
            <div>
              <h1>当前求和为:{this.props.count}</h1>
              <select ref={(cur) => (this.selectNumber = cur)}>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
              </select>
              &nbsp;&nbsp;
              <button onClick={this.increment}>+</button>
              &nbsp;&nbsp;
              <button onClick={this.decrement}>-</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button>
              &nbsp;&nbsp;
              <button onClick={this.incrementAsync}>异步加</button>
            </div>
          );
        }
      }
      
      /**
       * 拆分函数写法
      // 映射状态
      const mapStateToProps = (state) => ({ count: state });
      // 映射操作状态的方法
      const mapDispatchToProps = (dispatch) => ({
        add: (data) => dispatch(createIncrementAction(data)),
        delete: (data) => dispatch(createDecrementAction(data)),
        addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)),
      });
      
      // 使用connect()()创建并暴露一个Count的容器组件
      export default connect(mapStateToProps, mapDispatchToProps)(Count);
      */
      
      /**
       * 合并函数写法
       */
      // 使用connect()()创建并暴露一个Count的容器组件
      export default connect(
        (state) => ({ count: state }),
        // mapDispatchToProps 的一般写法
        // (dispatch) => ({
        //   add: (data) => dispatch(createIncrementAction(data)),
        //   delete: (data) => dispatch(createDecrementAction(data)),
        //   addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)),
        // })
      
        // mapDispatchToProps 的简写
        {
          add: createIncrementAction,
          delete: createDecrementAction,
          addAsync: createIncrementAsyncAction,
        }
      )(Count);
      
      • 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
    2. 修改 App.jsx,无需自己给容器组件传递store

      import React, { Component } from "react";
      import Count from "./containers/Count";
      
      export default class App extends Component {
        render() {
          return (
            <div>
              <Count/>
            </div>
          );
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    3. 修改 index.js 文件

      1.去除store.subscribe(() => { });状态监听
      2.给包裹一个标签
      Provider:让所有组件都可以得到state数据

      import React from "react";
      import reactDOM from "react-dom";
      import App from "./App";
      import store from "./redux/store";
      import { Provider } from "react-redux";
      reactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById("root")
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

    七. react-redux 数据共享(在 react-redux 的优化使用 中修改)

    1. 修改 src/redux/constant.js 文件,新增常量值
      export const INCREMENT = "increment";
      export const DECREMENT = "decrement";
      export const ADD_PERSON = "add_person";
      
      • 1
      • 2
      • 3
    2. src/redux 文件夹中创建一个 actions 文件夹,将所有的 action文件 放入。在其中创建一个person 的 action文件,用于为 person组件reducer服务
      import { ADD_PERSON } from "../constant";
      
      // 创建增加一个人的action对象
      export const creactAddPersonAction = (personObj) => {
        return { type: ADD_PERSON, data: personObj };
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. src/redux 文件夹中创建一个 reducers 文件夹,将所有的 reducers文件 放入。在其中创建一个 person 的reducers文件
      import { ADD_PERSON } from "../constant";
      // 初始化人员列表
      const initState = [{ id: 1, name: "tom", age: 20 }];
      export default function personReducer(preState = initState, action) {
        console.log('person')
        const { type, data } = action;
        switch (type) {
          // 添加
          case ADD_PERSON:
            return [data, ...preState];
          default:
            return preState;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    4. 修改 store.js 文件,引入 combineReducers ,用于汇总所有的reducer,变为一个整的reducer
      /**
       * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
       */
      
      // 引入createStore,专门用于创建redux中最为核心的store对象
      import { createStore, applyMiddleware,combineReducers } from "redux";
      // 引入为count组件服务的reducer
      import countReducer from "./reducers/count";
      // 引入Person组件服务的reducer
      import personReducer from './reducers/person'
      // 引入redux-thunk,用于支持异步action
      import thunk from "redux-thunk";
      
      // 汇总所有的reducer,变为一个整的reducer
      const allReducer = combineReducers({
          total:countReducer,
          persons:personReducer
      })
      const store = createStore(allReducer, applyMiddleware(thunk));
      export default store;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
    5. 新增一个 Person 组件,和Count组件通过 redux 共享数据

      取出 redux 状态时,要取到对象组件的key值

      import React, { Component } from "react";
      import { connect } from "react-redux";
      import { creactAddPersonAction } from "../../redux/actions/person";
      
      class Person extends Component {
        addPerson = () => {
          const name = this.nameNode.value;
          const age = this.ageNode.value;
          const id = this.props.personList.length + 1;
          let personObj = {
            id,
            name,
            age,
          };
          this.props.addPerson(personObj);
          this.nameNode.value = "";
          this.ageNode.value = "";
        };
        render() {
          return (
            <div>
              <h2>Person 组件</h2>
              <h4>上方Count组件的总和为:{this.props.total}</h4>
              <input
                ref={(cur) => (this.nameNode = cur)}
                type="text"
                placeholder="请输入姓名"
              />
              <input
                ref={(cur) => (this.ageNode = cur)}
                type="text"
                placeholder="请输入年龄"
              />
              <button onClick={this.addPerson}>添加</button>
              <ul>
                {this.props.personList.map((v) => {
                  return (
                    <li key={v.id}>
                      姓名:{v.name},年龄:{v.age}
                    </li>
                  );
                })}
              </ul>
            </div>
          );
        }
      }
      
      const mapStateToProps = (state) => ({
        personList: state.persons,
        total: state.total,
      });
      
      const mapDispatchToProps = {
        addPerson: creactAddPersonAction,
      };
      
      export default connect(mapStateToProps, mapDispatchToProps)(Person);
      
      • 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

    八. react-redux 总体优化(在 react-redux 数据共享 中修改)

    1. 所有的变量名尽可能规范,并触发对象的简写方式
      • 汇总 reducer
      // 引入combineReducers,用于汇总所有的reducer
      import { combineReducers } from "redux";
      // 引入为count组件服务的reducer
      import count from "./count";
      // 引入Person组件服务的reducer
      import persons from "./person";
      // 汇总所有的reducer,变为一个整的reducer
      const allReducer = combineReducers({
        count,
        persons,
      });
      
      export default allReducer;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 组件中使用 action 方法时
      import { addPerson } from "../../redux/actions/person";
      const mapStateToProps = (state) => ({
        personList: state.persons,
        total: state.count,
      });
      
      const mapDispatchToProps = {
        addPerson,
      };
      
      export default connect(mapStateToProps, mapDispatchToProps)(Person);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    2. reducer文件夹中,新增一个index.js文件,专门用于汇总并暴露所有的reducer
      /**
       * 该文件用于汇总所有的reducer为一个总的reducer
       */
      // 引入combineReducers,用于汇总所有的reducer
      import { combineReducers } from "redux";
      // 引入为count组件服务的reducer
      import count from "./count";
      // 引入Person组件服务的reducer
      import persons from "./person";
      // 汇总所有的reducer,变为一个整的reducer
      const allReducer = combineReducers({
        count,
        persons,
      });
      
      export default allReducer;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      store.js 文件中使用
      // 引入汇总之后的reducer
      import allReducer from "./reducers";
      
      const store = createStore(
        allReducer,
        applyMiddleware(thunk)
      );
      export default store;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    九. react-redux 开发者工具的使用

    1. 浏览器拓展程序引入 Redux DevTools
      在这里插入图片描述

    2. 项目中安装 redux-devtools-extension
      yarn add redux-devtools-extension

    3. 配置store.js文件

      // 引入 redux-devtools-extension
      import { composeWithDevTools } from "redux-devtools-extension";
      const store = createStore(
          allReducer,
          composeWithDevTools(applyMiddleware(thunk))
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    十. react-redux 正规使用流程

    1. 准备工作,创建 redux 所需文件

    src 文件夹下创建一个 redux 文件夹
    redux 文件夹下创建 actions 文件夹、reducers 文件夹、constans.js 文件、store.js 文件
    reducers 文件夹下创建 index.js 文件

    redux 文件层级:

    - src
      - redux
        - actions
    	- reducers
    	  - index.js
    	- constans.js
    	- store.js
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • actions 文件夹
      用于为组件生成 action 对象。
    • reducers 文件夹
      (1)用于创建一个组件服务的 reducerreducer 的本质是一个函数。
      (2)reducer 函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action)。
    • reducers/index.js 文件
      该文件用于汇总所有的 reducer 为一个总的 reducer
    • constans.js 文件
      (1)用于定义 action 对象中 type 类型的常量值。
      (2)目的只有一个:便于管理的同时防止程序员单词写错。
    • store.js 文件
      该文件专门用于暴露一个 store 对象,整个应用只有一个store 对象。

    2. 安装 redux 所需的依赖,编写基础代码

    1. 安装 redux
      用于创建redux中最为核心的store对象
      yarn add redux react-redux
    2. 安装 redux-thunk
      用于支持异步 action
      yarn add redux-thunk
    3. 安装 redux-devtools-extension
      用于支持 react-redux 开发者工具的使用
      yarn add redux-devtools-extension
    • src/redux/reducers/index.js 文件修改
      // 引入combineReducers,用于汇总所有的reducer
      import { combineReducers } from "redux";
      
      // 汇总所有的reducer,变为一个整的reducer
      const allReducer = combineReducers({
      
      });
      
      export default allReducer;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • src/redux/store.js 文件修改
      // 引入createStore,专门用于创建redux中最为核心的store对象
      import { createStore, applyMiddleware } from "redux";
      // 引入redux-thunk,用于支持异步action
      import thunk from "redux-thunk";
      // 引入 redux-devtools-extension
      import { composeWithDevTools } from "redux-devtools-extension";
      // 引入汇总之后的reducer
      import allReducer from "./reducers";
      
      const store = createStore(
        allReducer,
        composeWithDevTools(applyMiddleware(thunk))
      );
      export default store;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • src/index.js 文件修改
      import React from "react";
      import ReactDOM from "react-dom/client";
      import App from "./App";
      import reportWebVitals from "./reportWebVitals";
      import { Provider } from "react-redux";
      import store from "./redux/store";
      
      const root = ReactDOM.createRoot(document.getElementById("root"));
      root.render(
        <React.StrictMode>
          {/* 此处需要用 Provider 包裹 App,目的是让 App 所有的后代容器组件都能接收到store */}
          {/* 可代替下方 store.subscribe(),实现自动监听 */}
          <Provider store={store}>
            <App />
          </Provider>
        </React.StrictMode>
      );
      // 监测redux中状态的改变, 若redux中的状态发生了改变,则重新渲染App组件
      // store.subscribe(() => {
      //   root.render(
      //     
      //       
      //     
      //   );
      // });
      reportWebVitals();
      
      • 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

    3. 编写组件功能

    组件的写法有两种: 类组件函数组件
    下面分别讲一下 rudex类组件函数组件 中的写法

    connect 写法:

    export default connect(
        (state) => ({ key: value }),//映射状态
        { key: action方法, }//映射操作状态的方法
    )(UI组件);
    
    • 1
    • 2
    • 3
    • 4

    3.1 类组件 与 函数组件 写法

    3.1.1 类组件中写法
    import React, { Component } from "react";
    import { connect } from "react-redux";
    
    export class index extends Component {
      render() {
        return <div>index</div>;
      }
    }
    /**
     * 映射状态,组件中需要使用的 reducer 状态
     * state.xxx  key 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key
     */
    const mapStateToProps = (state) => ({});
    // 映射操作状态的方法,组件中需要使用的事件方法
    const mapDispatchToProps = {};
    // 使用connect()()创建并暴露一个Count的容器组件
    export default connect(mapStateToProps, mapDispatchToProps)(index);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3.1.2 函数组件中写法
    import React from "react";
    import { connect } from "react-redux";
    
    export const index = (props) => {
      // props 等同于 类组件中的 this.props
      return <div>index</div>;
    };
    
    /**
     * 映射状态,组件中需要使用的 reducer 状态
     * state.xxx  key 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key
     */
    const mapStateToProps = (state) => ({});
    // 映射操作状态的方法,组件中需要使用的事件方法
    const mapDispatchToProps = {};
    // 使用connect()()创建并暴露一个Count的容器组件
    export default connect(mapStateToProps, mapDispatchToProps)(index);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2 组件中使用实例

    分别在类组件、函数组件中 使用redux

    3.2.1 Count 组件(类组件写法)

    实现一个数值 加减 的组件(Count 组件)

    1. src/redux/constants.js 文件修改,添加方法常量

      // 定义action对象中type类型的常量值
      export const INCREMENT = "increment";
      export const DECREMENT = "decrement";
      
      • 1
      • 2
      • 3
    2. src/redux/actions 文件夹新增 count.js,对应 Count 组件action

      import { INCREMENT, DECREMENT } from "../constants.js";
      
      // 加
      // 简写方式
      // export const increment = (data) => ({ type: INCREMENT, data });
      // 正常方式
      export const increment = (data) => {
        return {
          type: INCREMENT,
          data,
        };
      };
      // 减
      export const decrement = (data) => {
        return {
          type: DECREMENT,
          data,
        };
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    3. src/redux/reducers 文件夹新增 count.js,对应 Count 组件reducer

      /**
       * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数
       * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action)
       */
      import { DECREMENT, INCREMENT } from "../constants.js";
      
      const initState = 0; //初始化状态
      // 当第一次 preState 为 undefined 时,preState 赋值等于 initState
      export default function increment(preState = initState, action) {
        // 从 action 对象中获取:type,data
        const { type, data } = action;
        // 根据 type 决定加工数据
        switch (type) {
          case INCREMENT:
            return preState + data;
          case DECREMENT:
            return preState - data;
          default:
            return preState;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    4. src/redux/reducers/index.js 文件修改,添加合并 Count 组件reducer
      在这里插入图片描述

    5. Count 组件中 引入 connect 生成一个容器组件,并暴露。通过this.props....读取和操作状态。

      容器组件写法:

      export default connect(
          (state) => ({ key: value }),//映射状态
          { key: action方法, }//映射操作状态的方法
      )(UI组件);
      
      • 1
      • 2
      • 3
      • 4
      import React, { Component } from "react";
      import { connect } from "react-redux";
      import { increment, decrement } from "../../redux/actions/count";
      
      class Count extends Component {
        incrementNumber = () => {
          this.props.increment(1);
        };
        decrementNumber = () => {
          this.props.decrement(1);
        };
        render() {
          console.log(this.props);
          return (
            <div>
              <h4>当前数值:{this.props.num}</h4>
              <button onClick={this.incrementNumber}>+1</button>
              <button onClick={this.decrementNumber}>-1</button>
            </div>
          );
        }
      }
      /**
       * 映射状态,组件中需要使用的 reducer 状态
       * state.xxx  key 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key
       */
      const mapStateToProps = (state) => ({ 
        num: state.count 
      });
      // 映射操作状态的方法,组件中需要使用的事件方法
      const mapDispatchToProps = {
        increment,
        decrement,
      };
      // 使用connect()()创建并暴露一个Count的容器组件
      export default connect(mapStateToProps, mapDispatchToProps)(Count);
      
      • 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
    3.2.2 Persons 组件(函数组件写法)

    实现一个人员信息列表 新增 功能(Persons 组件)
    步骤同 3.1 Conut 组件实例 相同

    1. src/redux/constants.js 文件修改,添加方法常量

      // 定义action对象中type类型的常量值
      export const INCREMENT = "increment";
      export const DECREMENT = "decrement";
      
      export const ADD_PERSON = "add_person";
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. src/redux/actions 文件夹新增 persons.js,对应 Persons 组件action

      import { ADD_PERSON } from "../constants.js";
      
      // 新增人员
      export const addPerson = (data) => ({ type: ADD_PERSON, data });
      
      • 1
      • 2
      • 3
      • 4
    3. src/redux/reducers 文件夹新增 persons.js,对应 Persons 组件reducer

      import { ADD_PERSON } from "../constants.js";
      
      const initState = [
        {
          id: 1,
          name: "小明",
          age: 18,
        },
      ];
      export default function addPerson(preState = initState, action) {
        const { type, data } = action;
        // 根据 type 决定加工数据
        switch (type) {
          case ADD_PERSON:
            return [data, ...preState];
          default:
            return preState;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    4. src/redux/reducers/index.js 文件修改,添加合并 Persons 组件reducer
      在这里插入图片描述

    5. Persons 组件 中 引入 connect 生成一个容器组件,并暴露。通过props....读取和操作状态。

      import React from "react";
      import { useRef } from "react";
      import { connect } from "react-redux";
      // 引入 action
      import { addPerson } from "../../redux/actions/persons.js";
      
      export const Persons = (props) => {
        // props 等同于 类组件中的 this.props
        console.log(props);
        const nameRef = useRef();
        const ageRef = useRef();
        function add() {
          const name = nameRef.current.value;
          const age = ageRef.current.value * 1;
          const personObj = {
            id: props.personList.length + 1,
            name,
            age,
          };
          props.addPerson(personObj);
        }
      
        return (
          <div>
            <input ref={nameRef} type="text" placeholder="请输入姓名" />
            &nbsp;
            <input ref={ageRef} type="number" placeholder="请输入年龄" />
            &nbsp;
            <button onClick={add}>添加</button>
            <ul>
              {props.personList.map((v) => {
                return (
                  <li key={v.id}>
                    姓名:{v.name},年龄:{v.age}
                  </li>
                );
              })}
            </ul>
          </div>
        );
      };
      
      /**
       * 映射状态,组件中需要使用的 reducer 状态
       * state.xxx  xxx 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key
       */
      const mapStateToProps = (state) => ({
        personList: state.persons,
      });
      // 映射操作状态的方法,组件中需要使用的事件方法
      const mapDispatchToProps = {
        addPerson,
      };
      // 使用connect()()创建并暴露一个Persons的容器组件
      export default connect(mapStateToProps, mapDispatchToProps)(Persons);
      
      • 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
    3.2.3 Count组件 和 Persons 组件 相互通信
    1. Count组件

      mapStateToProps 中映射 Person组件 的状态
      在这里插入图片描述
      render() 中渲染
      在这里插入图片描述

    2. Persons组件

      mapStateToProps 中映射 Count组件 的状态
      在这里插入图片描述
      视图渲染:
      在这里插入图片描述

    3.2.4 页面展示

    在这里插入图片描述

  • 相关阅读:
    Java Tomcat内存马——filter内存马
    【教程】Derby数据库安装与使用
    Xilinx FPGA SPIx4 配置速度50M约束语句(Vivado开发环境)
    怎么简单实现菜单拖拽排序的功能
    systemverilog:interface中端口方向、Clocking block的理解
    【C++】智能指针
    SpringBoot 整合多数据源的事务问题
    LFS(Linux From Scratch)构建过程全记录(七):进入Chroot并构建临时工具
    简单的 JSONParser
    word可以画神经网络图吗,如何画神经网络结构图
  • 原文地址:https://blog.csdn.net/Jie_1997/article/details/128078971