• React 全栈体系(十四)


    第七章 redux

    六、react-redux

    7. 代码 - react-redux 数据共享版

    7.1 效果

    请添加图片描述

    7.2 App
    /* src/App.jsx */
    import React, { Component } from "react";
    import Count from "./containers/Count";
    import Person from "./containers/Person";
    
    export default class App extends Component {
      render() {
        return (
          <div>
            <Count />
            <hr />
            <Person />
          </div>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    7.3 Count
    /* src/containers/Count/index.jsx */
    import React, { Component } from "react";
    import {
      createIncrementAction,
      createDecrementAction,
      createIncrementAsyncAction,
    } from "../../redux/actions/count";
    
    //引入connect用于连接UI组件与redux
    import { connect } from "react-redux";
    
    // 定义UI组件
    class Count extends Component {
      //加法
      increment = () => {
        const { value } = this.selectNumber;
        this.props.jia(value * 1);
      };
      //减法
      decrement = () => {
        const { value } = this.selectNumber;
        this.props.jian(value * 1);
      };
      //奇数加
      incrementIfOdd = () => {
        const { value } = this.selectNumber;
        if (this.props.count % 2 !== 0) {
          this.props.jia(value * 1);
        }
      };
      //异步加
      incrementAsync = () => {
        const { value } = this.selectNumber;
        this.props.jiaAsync(value * 1, 500);
      };
      render() {
        return (
          <div>
            <h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2>
            <h4>
              当前求和为:{this.props.count}
            </h4>
            <select ref={(c) => (this.selectNumber = c)}>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
            </select>
            &nbsp;
            <button onClick={this.increment}>+</button>&nbsp;
            <button onClick={this.decrement}>-</button>&nbsp;
            <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
            <button onClick={this.incrementAsync}>异步加</button>
          </div>
        );
      }
    }
    
    //使用connect()()创建并暴露一个Count的容器组件
    export default connect(
      (state) => ({ count: state.he, renshu: state.rens.length }),
      {
        jia: createIncrementAction,
        jian: createDecrementAction,
        jiaAsync: 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
    7.4 Person
    /* src/containers/Person/index.jsx */
    import React, { Component } from "react";
    import { nanoid } from "nanoid";
    import { connect } from "react-redux";
    import { createAddPersonAction } from "../../redux/actions/person";
    
    class Person extends Component {
      addPerson = () => {
        const name = this.nameNode.value;
        const age = this.ageNode.value;
        const personObj = { id: nanoid(), name, age };
        this.props.jiaYiRen(personObj);
        this.nameNode.value = "";
        this.ageNode.value = "";
      };
      render() {
        return (
          <div>
            <h2>我是Person组件,上方组件求和为{this.props.he}</h2>
            <input
              ref={(c) => (this.nameNode = c)}
              type="text"
              placeholder="输入名字"
            />
            <input
              ref={(c) => (this.ageNode = c)}
              type="text"
              placeholder="输入年龄"
            />
            <button onClick={this.addPerson}>添加</button>
            <ul>
              {this.props.yiduiren.map((p) => {
                return (
                  <li key={p.id}>
                    {p.name}--{p.age}
                  </li>
                );
              })}
            </ul>
          </div>
        );
      }
    }
    
    export default connect(
      (state) => ({ yiduiren: state.rens, he: state.he }), //映射状态
      { jiaYiRen: createAddPersonAction } //映射操作状态的方法
    )(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
    7.5 store
    /* src/redux/store.js */
    /**
     * 该文件专门用于暴露一个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({
      he: countReducer,
      rens: personReducer,
    });
    //暴露store
    export default createStore(allReducer, applyMiddleware(thunk));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    7.6 constant
    /* src/redux/constant.js */
    /**
     * 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
     */
    export const INCREMENT = "increment";
    export const DECREMENT = "decrement";
    export const ADD_PERSON = "add_person";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    7.7 actions
    7.5.1 count
    /* src/redux/actions/count.js */
    /**
     * 该文件专门为Count组件生成action对象
     */
    import { INCREMENT, DECREMENT } from "../constant";
    
    //同步action,就是指action的值为Object类型的一般对象
    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(() => {
          dispatch(createIncrementAction(data));
        }, time);
      };
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    7.5.2 person
    /* src/redux/actions/person.js */
    import { ADD_PERSON } from "../constant";
    
    //创建增加一个人的action动作对象
    export const createAddPersonAction = (personObj) => ({
      type: ADD_PERSON,
      data: personObj,
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    7.8 reducers
    7.8.1 count
    /* src/redux/reducers/count.js */
    /**
     * 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
     * 2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
     */
    import { INCREMENT, DECREMENT } from "../constant";
    
    const initState = 0;
    export default function countReducer(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
    7.8.2 person
    /* src/redux/reducers/person.js */
    import { ADD_PERSON } from "../constant";
    
    //初始化人的列表
    const initState = [{ id: "001", name: "tom", age: 18 }];
    
    export default function personReducer(preState = initState, action) {
      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
    • 15
    7.9 总结
    (1).定义一个Person组件,和Count组件通过redux共享数据。
    (2).为Person组件编写:reducer、action,配置constant常量。
    (3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,
    		合并后的总状态是一个对象!!!
    (4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    七、纯函数和高阶函数

    1. 纯函数

    • 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
    • 必须遵守以下一些约束
      • 不得改写参数数据
      • 不会产生任何副作用,例如网络请求,输入和输出设备
      • 不能调用 Date.now()或者 Math.random()等不纯的方法
    • redux 的 reducer 函数必须是一个纯函数

    2. 高阶函数

    • 理解: 一类特别的函数
      • 情况 1: 参数是函数
      • 情况 2: 返回是函数
    • 常见的高阶函数:
      • 定时器设置函数
      • 数组的 forEach()/map()/filter()/reduce()/find()/bind()
      • promise
      • react-redux 中的 connect 函数
    • 作用: 能实现更加动态, 更加可扩展的功能

    八、使用 redux 调试工具

    1. 安装 chrome 浏览器插件

    请添加图片描述

    2. 下载工具依赖包

    • npm install --save-dev redux-devtools-extension

    3. 总结

    (1).yarn add redux-devtools-extension
    (2).store中进行配置
    	import {composeWithDevTools} from 'redux-devtools-extension'
    	const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
    
    • 1
    • 2
    • 3
    • 4

    九、react-redux 最终版

    请添加图片描述

    1. index.js

    /* src/index.js */
    //引入react核心库
    import React from "react";
    //引入ReactDOM
    import ReactDOM from "react-dom";
    //引入App组件
    import App from "./App";
    import store from "./redux/store";
    import { Provider } from "react-redux";
    
    ReactDOM.render(
      /** 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2. App.jsx

    /* src/App.jsx */
    import React, { Component } from "react";
    import Count from "./containers/Count"; //引入的Count容器组件
    import Person from "./containers/Person"; //引入的Person容器组件
    
    export default class App extends Component {
      render() {
        return (
          <div>
            <Count />
            <hr />
            <Person />
          </div>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3. store.js

    /* src/redux/store.js */
    /**
     * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
     */
    //引入createStore,专门用于创建redux中最为核心的store对象
    import { createStore, applyMiddleware } from "redux";
    //引入汇总之后的reducer
    import reducer from "./reducers";
    //引入redux-thunk,用于支持异步action
    import thunk from "redux-thunk";
    
    //暴露store
    export default createStore(reducer, applyMiddleware(thunk));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4. containers

    4.1 Count
    /* src/containers/Count/index.jsx */
    import React, { Component } from "react";
    import {
      increment,
      decrement,
      incrementAsync,
    } from "../../redux/actions/count";
    
    //引入connect用于连接UI组件与redux
    import { connect } from "react-redux";
    
    // 定义UI组件
    class Count extends Component {
      //加法
      increment = () => {
        const { value } = this.selectNumber;
        this.props.increment(value * 1);
      };
      //减法
      decrement = () => {
        const { value } = this.selectNumber;
        this.props.decrement(value * 1);
      };
      //奇数加
      incrementIfOdd = () => {
        const { value } = this.selectNumber;
        if (this.props.count % 2 !== 0) {
          this.props.increment(value * 1);
        }
      };
      //异步加
      incrementAsync = () => {
        const { value } = this.selectNumber;
        this.props.incrementAsync(value * 1, 500);
      };
      render() {
        return (
          <div>
            <h2>我是Count组件,下方组件总人数为:{this.props.personCount}</h2>
            <h4>当前求和为:{this.props.count}</h4>
            <select ref={(c) => (this.selectNumber = c)}>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
            </select>
            &nbsp;
            <button onClick={this.increment}>+</button>&nbsp;
            <button onClick={this.decrement}>-</button>&nbsp;
            <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
            <button onClick={this.incrementAsync}>异步加</button>
          </div>
        );
      }
    }
    
    //使用connect()()创建并暴露一个Count的容器组件
    export default connect(
      (state) => ({
        count: state.count,
        personCount: state.persons.length,
      }),
      {
        increment,
        decrement,
        incrementAsync,
      }
    )(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
    4.2 Person
    /* src/containers/Person/index.jsx */
    import React, { Component } from "react";
    import { nanoid } from "nanoid";
    import { connect } from "react-redux";
    import { addPerson } from "../../redux/actions/person";
    
    class Person extends Component {
      addPerson = () => {
        const name = this.nameNode.value;
        const age = this.ageNode.value * 1;
        const personObj = { id: nanoid(), name, age };
        this.props.addPerson(personObj);
        this.nameNode.value = "";
        this.ageNode.value = "";
      };
      render() {
        return (
          <div>
            <h2>我是Person组件,上方组件求和为{this.props.count}</h2>
            <input
              ref={(c) => (this.nameNode = c)}
              type="text"
              placeholder="输入名字"
            />
            <input
              ref={(c) => (this.ageNode = c)}
              type="text"
              placeholder="输入年龄"
            />
            <button onClick={this.addPerson}>添加</button>
            <ul>
              {this.props.personArr.map((p) => {
                return (
                  <li key={p.id}>
                    {p.name}--{p.age}
                  </li>
                );
              })}
            </ul>
          </div>
        );
      }
    }
    
    export default connect(
      (state) => ({
        personArr: state.persons,
        count: state.count,
      }), //映射状态
      { addPerson } //映射操作状态的方法
    )(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

    5. reducers

    5.1 index
    /* src/redux/reducers/index.js */
    /**
     * 该文件用于汇总所有的reducer为一个总的reducer
     */
    //引入combineReducers,用于汇总多个reducer
    import { combineReducers } from "redux";
    
    //引入为Count组件服务的reducer
    import count from "./count";
    //引入为Person组件服务的reducer
    import persons from "./person";
    
    //汇总所有的reducer变为一个总的reducer
    export default combineReducers({
      count,
      persons
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    5.2 count
    /* src/redux/reducers/count.js */
    /**
     * 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
     * 2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
     */
    import { INCREMENT, DECREMENT } from "../constant";
    
    const initState = 0;
    export default function countReducer(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
    5.3 person
    /* src/redux/reducers/person.js */
    import { ADD_PERSON } from "../constant";
    
    //初始化人的列表
    const initState = [{ id: "001", name: "tom", age: 18 }];
    
    export default function personReducer(preState = initState, action) {
      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
    • 15

    6. actions

    6.1 count
    /* src/redux/actions/count.js */
    /**
     * 该文件专门为Count组件生成action对象
     */
    import { INCREMENT, DECREMENT } from "../constant";
    
    //同步action,就是指action的值为Object类型的一般对象
    export const increment = (data) => ({ type: INCREMENT, data });
    export const decrement = (data) => ({ type: DECREMENT, data });
    
    //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
    export const incrementAsync = (data, time) => {
      return (dispatch) => {
        setTimeout(() => {
          dispatch(increment(data));
        }, time);
      };
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    6.2 person
    /* src/redux/actions/person.js */
    import { ADD_PERSON } from "../constant";
    
    //创建增加一个人的action动作对象
    export const addPerson = (personObj) => ({
      type: ADD_PERSON,
      data: personObj,
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    7. 总结

    (1).所有变量名字要规范,尽量触发对象的简写形式。
    (2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
    
    • 1
    • 2

    十、项目打包

    npm run build
    
    npm install -g serve
    serve -s build
    
    • 1
    • 2
    • 3
    • 4

    请添加图片描述

  • 相关阅读:
    postCss基本介绍
    Java【算法 05】通过时间获取8位验证码(每两个小时生成一个)源码分享
    鸿蒙系统适配的流程
    【Java项目推荐之黑马头条】自媒体文章实现异步上下架(使用Kafka中间件实现)
    氨基NH2修饰ZnTe量子点|羧酸COOH功能化CdSe/CdS量子点|马来酰亚胺MAL偶联CdTe/CdS量子点
    LVS,Nginx,Haproxy三种负载均衡产品的对比
    Spring定时任务+webSocket实现定时给指定用户发送消息
    Nginx代理websocket为什么要这样做?
    Bootstrap Blazor Table 组件(三)智能生成
    【iOS逆向与安全】某音App直播间自动发666 和 懒人自动看视频
  • 原文地址:https://blog.csdn.net/sgsgkxkx/article/details/133189340