中文文档: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({});
        });
    }
    
    (2)在 index.js 文件中检测 store 中状态的改变,一旦发生改变重新渲染<App/>
    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"));
    });
    

二. 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;
    
  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;
      }
    }
    
  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>
        );
      }
    }
    

三. 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";
    
  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 });
    
  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;
      }
    }
    
  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>
        );
      }
    }
    

四. 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;
    
  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);
      };
    };
    
  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);
    };
    

五. 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);
    
  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>
        );
      }
    }
    
  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.将 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);
    
  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>
        );
      }
    }
    
  3. 修改 index.js 文件

    1.去除store.subscribe(() => { });状态监听
    2.给<App/>包裹一个<Provider store={store}></Provider>标签
    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")
    );
    

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

  1. 修改 src/redux/constant.js 文件,新增常量值
    export const INCREMENT = "increment";
    export const DECREMENT = "decrement";
    export const ADD_PERSON = "add_person";
    
  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 };
    };
    
  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;
      }
    }
    
  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;
    
  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);
    

八. 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;
    
    • 组件中使用 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);
    
  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;
    
    store.js 文件中使用
    // 引入汇总之后的reducer
    import allReducer from "./reducers";
    
    const store = createStore(
      allReducer,
      applyMiddleware(thunk)
    );
    export default store;
    

九. 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))
    );
    

十. 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
  • 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;
    
  • 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;
    
  • 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(
    //     <React.StrictMode>
    //       <App />
    //     </React.StrictMode>
    //   );
    // });
    reportWebVitals();
    

3. 编写组件功能

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

connect 写法:

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

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);
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);

3.2 组件中使用实例

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

3.2.1 Count 组件(类组件写法)

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

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

    // 定义action对象中type类型的常量值
    export const INCREMENT = "increment";
    export const DECREMENT = "decrement";
    
  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,
      };
    };
    
  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;
      }
    }
    
  4. src/redux/reducers/index.js 文件修改,添加合并 Count 组件reducer
    在这里插入图片描述

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

    容器组件写法:

    export default connect(
        (state) => ({ key: value }),//映射状态
        { key: action方法, }//映射操作状态的方法
    )(UI组件);
    
    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);
    
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";
    
  2. src/redux/actions 文件夹新增 persons.js,对应 Persons 组件action

    import { ADD_PERSON } from "../constants.js";
    
    // 新增人员
    export const addPerson = (data) => ({ type: ADD_PERSON, data });
    
  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;
      }
    }
    
  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);
    
3.2.3 Count组件 和 Persons 组件 相互通信
  1. Count组件

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

  2. Persons组件

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

3.2.4 页面展示

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐