React - redux 使用(由浅入深)

news2024/11/13 21:26:21

React - redux 使用(由浅入深)

  • 一. redux理解
    • 1. redux 介绍
    • 2. redux 使用情况
    • 3. redux 工作流程
    • 4. redux 三个核心概念
      • 4.1 Action
      • 4.2 Store
      • 4.3 Reducers
    • 5. redux 核心API
      • 5.1 createStore()
      • 5.2 Store
        • 5.2.1 Store 方法
          • 5.2.1.1 getState()
          • 5.2.1.2 dispatch(action)
          • 5.2.1.3 subscribe(listener)
  • 二. redux精简版使用(不使用 Action)
  • 三. redux完整版使用(在 redux精简版基础上使用 Action)
  • 四. redux 异步action(在 redux完整版基础上使用 异步action)
  • 五. react-redux 的基本使用(在 redux 异步action修改使用 react-redux)
  • 六. react-redux 的优化使用(在 react-redux 的基本使用 中修改)
  • 七. react-redux 数据共享(在 react-redux 的优化使用 中修改)
  • 八. react-redux 总体优化(在 react-redux 数据共享 中修改)
  • 九. react-redux 开发者工具的使用
  • 十. react-redux 正规使用流程
    • 1. 准备工作,创建 redux 所需文件
    • 2. 安装 redux 所需的依赖,编写基础代码
    • 3. 编写组件功能
      • 3.1 Count 组件 实例
      • 3.2 Persons 组件 实例
      • 3.3 Count组件 和 Persons 组件 相互通信
      • 3.4 页面展示

中文文档: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. 编写组件功能

3.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 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 生成一个容器组件,并暴露。通过this.props....读取和操作状态。

    import React, { Component } from "react";
    import { connect } from "react-redux";
    // 引入 action
    import { addPerson } from "../../redux/actions/persons.js";
    
    class Persons extends Component {
      add = () => {
        const name = this.nameNode.value;
        const age = this.ageNode.value * 1;
        const personObj = {
          id: this.props.personList.length + 1,
          name,
          age,
        };
        this.props.addPerson(personObj);
      };
      render() {
        console.log(this.props);
        return (
          <div>
            <input
              ref={(cur) => (this.nameNode = cur)}
              type="text"
              placeholder="请输入姓名"
            />
            &nbsp;
            <input
              ref={(cur) => (this.ageNode = cur)}
              type="number"
              placeholder="请输入年龄"
            />
            &nbsp;
            <button onClick={this.add}>添加</button>
            <ul>
              {this.props.personList.map((v) => {
                return (
                  <li key={v.id}>
                    姓名:{v.name},年龄:{v.age}
                  </li>
                );
              })}
            </ul>
          </div>
        );
      }
    }
    /**
     * 映射状态,组件中需要使用的 reducer 状态
     * state.xxx  key 对应 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.3 Count组件 和 Persons 组件 相互通信

  1. Count组件

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

  2. Persons组件

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

    render() 中渲染
    在这里插入图片描述

3.4 页面展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67872.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kotlin 开发Android app(十八):线程Thread和UI更新

多线程的好处是不言而喻的&#xff0c;它能帮我们刚更多的事情&#xff0c;同时干不同的事情在程序设计中是经常出现的&#xff0c;这种时候我们使用的是线程。 在Kotlin 中&#xff0c;使用的还是原先java 的那一套&#xff0c;用的还是Thread &#xff0c;可能是在java 中T…

翻译: 如何学习编译器:LLVM Edition

编译器和编程语言是一个很大的话题。您不能只选择学习路径并在某个时候完成它。有许多不同的区域&#xff0c;每个区域都是无穷无尽的。 在这里&#xff0c;我想分享一些有助于学习编译器的链接。这份清单不可能详尽无遗——每个人都很忙&#xff0c;没有人有时间阅读龙书。 …

前端知识大全之CSS

目录 一、概念讲解 学习CSS之前必学的HTML &#xff08;超链接&#xff09; 二、正文代码 1.行内样式 2.内部样式&#xff08;选择器&#xff09; 3.外部样式 4.样式的优先级 5.简单选择器之定义单个标签&#xff08;id&#xff09; 6.简单选择器之定义多个标签&#xff…

低代码平台,企业业务创新的最佳路径

数字化转型的必然趋势及面临的问题 数字经济时代&#xff0c;数字化转型是企业在行业赛道上领先的必经之路&#xff0c;然而&#xff0c;数字化转型升级的道路并不是畅通无阻的&#xff0c;也不是企业单枪匹马就能干成的&#xff0c;各个企业在转型过程中都或多或少会遇到技术…

MySQL-索引

一、介绍 索引是数据库对象之一&#xff0c;用于提高字段检索效率&#xff0c;使用者只需要对哪个表中哪些字段建立索引即可&#xff0c;其余什么都不做&#xff0c;数据库会自行处理。 索引提供指向存储在表的指定列中的数据值的指针&#xff0c;如同图书的目录&#xff0c;…

【MMDetection】MMDetection中AnchorGenerator学习笔记

文章目录初始化-AnchorGenerator()Anchor平移-grid_priors计算有效anchor-valid_flags参考文献初始化-AnchorGenerator() TASK_UTILS.register_module() class AnchorGenerator:def __init__(self, strides, ratios, scalesNone, base_sizesNone, scale_majorTrue, octave_bas…

numpy的部分通用函数浅谈

numpy的部分通用函数 1.数组算术运算符 运算符对应的通用函数描述np.add加法运算&#xff08;即112)-np.substract减法运算&#xff08;即3-21&#xff09;-np.negative负数运算&#xff08;即-2&#xff09;*Nnp.multiply乘法运算&#xff08;即2*36&#xff09;/np.divide除…

Optional用法与争议点

Optional用法与争议点 简介 要说Java中什么异常最容易出现&#xff0c;我想NullPointerException一定当仁不让&#xff0c;为了解决这种null值判断问题&#xff0c;Java8中提供了一个新的工具类Optional&#xff0c;用于提示程序员注意null值&#xff0c;并在特定场景中简化代…

软件测试8年,却被应届生踩在头上,是应届生太牛了,还是我们太弱了?

前几天有个朋友向我哭诉&#xff0c;说他在公司干了8年的软件测试&#xff0c;却被一个实习生代替了&#xff0c;该何去何从? 这是一个值得深思的问题&#xff0c;作为职场人员&#xff0c;我们确实该思考&#xff0c;我们的工作会被实习生代替吗?这是一个很尖锐的问题&…

MFC基于对话框——仿照Windows计算器制作C++简易计算器

目录 一、界面设计 二、设置成员变量 三、初始化成员变量 四、初始化对话框 ​五、添加控件代码 1.各个数字的代码&#xff08;0~9&#xff09; 2.清除功能的代码 3.退格功能的代码 4.加减乘除功能的代码 5.小数点功能的代码 6.正负号功能的代码 7.等于功能的代码…

算法day42|背包问题

目录 01背包问题 二维 01背包问题 一维 416. 分割等和子集 背包问题分为&#xff1a;01背包&#xff0c;完全背包&#xff0c;多种背包01背包指的是有n种物品&#xff0c;每种物品只能取一个完全背包指的是有n种物品,每种物品可以取无限个多种背包指的是有n种物品&#xff0c;每…

公众号网课搜题接口

公众号网课搜题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

常用的在线工具网站

1&#xff0c;在线Photoshop软件 https://www.uupoop.com/ PS在线图片编辑器是一个专业精简的在线ps图片照片制作处理软件工具,绿色免安装,免下载,直接在浏览器打开就可用它修正,调整和美化图像。 2&#xff0c;bilibili视频编辑器 https://bilibili.clipchamp.com/ 由哔哩哔哩…

(保姆级)国内1块钱注册火爆全网的OpenAI-ChatGPT机器人

下面有给出完整的注册流程。首先介绍一下它是什么&#xff0c;如果只想看注册往下翻&#xff01; 1块钱注册火爆全网的OpenAI-ChatGPT机器人OpengAI-ChatGPT能做什么如何注册1块钱收取验证码使用注册的账号登录ChatGPTOpengAI-ChatGPT能做什么 我作为一个程序员用了一段时间&a…

金蝶云星空生产管理(冲刺学习)

物料“基本”和“生产”相关属性字段介绍 物料属性&#xff1a;生产中常用的物料属性包括自制、委外、外购、虚拟、配置、特征。 自制&#xff1a;一般是指由企业自己生产的物料&#xff0c;一般会建BOM、生产订单&#xff1b; 委外&#xff1a;是指委托给其他加工单位进行加工…

DevTools 无法加载来源映射:无法加载 chrome-extension: 警告的原因以及如何去除(全网最全 最详细解决方案)

是类似这样的一个警告。每次都有看着还是挺难受的。 这个警告的原因是你的浏览器插件造成的。例如警告已经很明确的告诉你是chrome-extension&#xff0c;也就是谷歌插件的问题。后面的字符串其实就是这个插件的id。 chrome-extension://cfhdojbkjhnklbpkdaibdccddilifddb/br…

QT笔记——QSlider滑动条滚轮事件和点击鼠标位置事件问题

需求&#xff1a;我们需要对一个滑动条 滚轮事件 和 点击到滑动条的位置 实时显示 问题&#xff1a;其中在做的时候遇到了很多的问题&#xff0c;一开始感觉很简单&#xff0c;现在将这些问题记录下来 ui图&#xff1a; 问题1&#xff1a;处理QSlider 滚轮事件的时候 这里有…

AlphaFold2源码解析(8)--模型之三维坐标构建

AlphaFold2源码解析(8)–模型之三维坐标构建 这个模块我们讲解AlphaFold的Structure module模块&#xff0c;该结构模块将蛋白质结构的抽象表示映射为具体的三维原子坐标。 Evoformer的单一表征被用作初始单一表征siinitial{s^{initial}_i }siinitial​&#xff0c;siinitial∈…

同步整流 降压恒流 输入4-40V 功率可达40W 电流3.6A 原理图

◆PCB 布线参考PCB 布局应遵循如下规则以确保芯片的正常工作。1:功率线包括地线&#xff0c;LX线和VIN线应该尽量做到短、 直和宽。2:输入电容应尽可能靠近芯片管脚&#xff08;VIN 和 &#xff09;。输入电源引脚可增加一个 0.1uF 的陶瓷电容以增强芯片的抗高频噪声能力。3:功…

小迪-day13(MySQL注入)

一、information_schema information_schema 数据库跟 performance_schema 一样&#xff0c;都是 MySQL 自带的信息数据库。其中 performance_schema 用于性能分析&#xff0c;而 information_schema 用于存储数据库元数据(关于数据的数据)&#xff0c;例如数据库名、表名、列…