React - redux 使用(由浅入深)

这篇具有很好参考价值的文章主要介绍了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 工作流程

React - 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
    React - redux 使用(由浅入深)

  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
    React - redux 使用(由浅入深)

  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
    React - redux 使用(由浅入深)

  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组件 的状态
    React - redux 使用(由浅入深)
    render() 中渲染
    React - redux 使用(由浅入深)

  2. Persons组件

    mapStateToProps 中映射 Count组件 的状态
    React - redux 使用(由浅入深)
    视图渲染:
    React - redux 使用(由浅入深)

3.2.4 页面展示

React - redux 使用(由浅入深)文章来源地址https://www.toymoban.com/news/detail-469659.html

到了这里,关于React - redux 使用(由浅入深)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【由浅入深学习MySQL】之索引进阶

    本系列为:MySQL数据库详解,为千锋资深教学老师独家创作 致力于为大家讲解清晰MySQL数据库相关知识点,含有丰富的代码案例及讲解。如果感觉对大家有帮助的话,可以【关注】持续追更~ 文末有本文重点总结,技术类问题,也欢迎大家和我们沟通交流! 从今天开始本系列

    2024年02月05日
    浏览(42)
  • 由浅入深理解C#中的事件

    本文较长,给大家提供了目录,可以直接看自己感兴趣的部分。 前面介绍了C#中的委托,事件的很多部分都与委托类似。实际上,事件就像是专门用于某种特殊用途的简单委托,事件包含了一个私有的委托,如下图所示: 有关事件的私有委托需要了解的重要事项如下: 1、事

    2024年02月03日
    浏览(39)
  • 由浅入深介绍 Python Websocket 编程

    1.1 websocket 协议简介 Websocket协议是对http的改进,可以实现client 与 server之间的双向通信; websocket连接一旦建立就始终保持,直到client或server 中断连接,弥补了http无法保持长连接的不足,方便了客户端应用与服务器之间实时通信。 适用场景 html页面实时更新, 客户端的html页面

    2024年02月03日
    浏览(39)
  • 手拉手Vue组件由浅入深

    组件 (Component) 是 Vue.js 最强大的功能之一,它是html、css、js等的一个聚合体,封装性和隔离性非常强。 组件化开发:     1、将一个具备完整功能的项目的一部分分割多处使用     2、加快项目的进度     3、可以进行项目的复用 组件注册分为:全局注册和局部注册 目录

    2024年01月18日
    浏览(41)
  • Springboot3+EasyExcel由浅入深

    环境介绍 技术栈 springboot3+easyexcel 软件 版本 IDEA IntelliJ IDEA 2022.2.1 JDK 17 Spring Boot 3 EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。 他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。 官网https://easyexcel.opensource.ali

    2024年01月16日
    浏览(42)
  • 由浅入深了解机器学习和GPT原理

    我不是一个机器学习专家,本来是一名软件工程师,与人工智能的互动很少。我一直渴望深入了解机器学习,但一直没有找到适合自己的入门方式。这就是为什么,当谷歌在2015年11月开源TensorFlow时,我非常兴奋,知道是时候开始学习之旅了。不想过于夸张,但对我来说,这就

    2024年02月09日
    浏览(32)
  • 【由浅入深学MySQL】- MySQL连接查询

    本系列为:MySQL数据库详解,为千锋教育资深Java教学老师独家创作 致力于为大家讲解清晰MySQL数据库相关知识点,含有丰富的代码案例及讲解。如果感觉对大家有帮助的话,可以【点个关注】持续追更~ 文末有重点总结和福利内容! 技术类问题,也欢迎大家和我们沟通交流!

    2024年02月05日
    浏览(60)
  • 什么是感知机——图文并茂,由浅入深

    生活中常常伴随着各种各样的逻辑判断,比如看到远方天空中飘来乌云,打开手机看到天气预报说1小时后40%的概率下雨,此时时候我们常常会做出等会下雨,出门带伞的判断。 上述思考过程可以抽象为一个”与“的”神经逻辑“。当”看到乌云“和”天气预报40%下雨“同时

    2023年04月20日
    浏览(38)
  • 由浅入深剖析 Apollo(阿波罗)架构

    目录 一、介绍 二、架构和模块 三、架构剖析 1.最简架构  2. 高可用保障  3.多接口扩展 四、总结 Apollo(阿波罗)是携程框架部研发并开源的一款生产级的配置中心产品,它能够集中管理应用在不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的

    2024年02月13日
    浏览(37)
  • 由浅入深一步步了解什么是哈希(概念向)

    我们来重新认识一下数据查找的过程: 在顺序结构以及平衡树 中, 记录的关键码与其存储位置之间没有对应的关系 ,因此在 查找一个元素时,必须要经过关键码的多次比较 。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( l o g 2 N log_2 N l o g 2 ​ N ),搜索的效率取决

    2024年04月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包