React(react18)中组件通信05——redux ➕ react-redux(含数据共享)

React(react18)中组件通信05——redux ➕ react-redux(含数据共享)

  • 1. 前言
    • 1.1 React中组件通信的其他方式
    • 1.2 介绍React-Redux
      • 1.2.1 简单介绍React-Redux
      • 1.2.2 官网
    • 1.3 安装react-redux
  • 2. 简单改写redux的例子
    • 2.1 提供store
    • 2.2 连接 Components + UI组件修改
      • 2.2.1 连接 Components
      • 2.2.2 修改UI组件
      • 2.2.3 看效果
    • 2.3 连接 Components——优化(优化容器组件)
    • 2.4 优化容器组件(可怕的精简)
    • 2.5 附代码
  • 3. 多reducer实现数据共享
    • 3.1 介绍 combineReducers()函数
    • 3.2 多reducer整合的例子
      • 3.2.1 想实现的效果
      • 3.2.2 项目目录设计结构
      • 3.2.3 将3个reducer整合(或者说拆分reducer)
      • 3.2.4 每个组件的三个核心文件
        • 3.2.4.1 简单介绍——以cat为例
        • 3.2.4.2 关于dog 和 petPark 的
    • 3.3 实现数据共享
      • 3.3.1 实现数据共享
    • 3.4 附核心代码
      • 3.4.1 两个action
      • 3.4.2 三个reducer + 一个整合reducer
      • 3.4.3 三个组件
  • 4. 附项目

1. 前言

1.1 React中组件通信的其他方式

  • React(react18)中组件通信01——props.
  • React(react18)中组件通信02——消息订阅与发布、取消订阅以及卸载组件时取消订阅.
  • React(react18)中组件通信03——简单使用 Context 深层传递参数.
  • React(react18)中组件通信04——redux入门.
    而本篇文章的代码改动是在此(redux入门)文章的基础上改动的,所以下面关于redux有疑问的还请看这篇文章。
  • React(react18)中组件通信06——redux-toolkit + react-redux.

1.2 介绍React-Redux

1.2.1 简单介绍React-Redux

  • React-Redux是Redux 官方提供的 React 绑定库。 具有高效且灵活的特性。
    • react-redux 是一个专为 React 应用开发而设计的基于 Redux 的库,提供了一些特定于 React 的功能和组件。
    • 它提供了一系列的 API 和组件,方便在 React 组件中使用 Redux 进行状态管理。
  • React-Redux 在概念上非常简单。它订阅 Redux 存储,检查组件所需的数据是否已更改,并重新渲染组件
  • react-redux 提供了一些特定于 React 的功能,如 connect 函数和 Provider 组件,用于连接 Redux 的 store,并将状态传递给 React 组件。
    • React Redux 提供的 组件,这使得 Redux store 能够在应用的其他地方使用(即:store只需在入口文件传递一次,其他需要store的容器组件中都可以获取)。

1.2.2 官网

  • 参考官网:
    • 官网地址:https://react-redux.js.org/.
    • gitHub上:https://github.com/reduxjs/react-redux.
    • Redux 中文官网.
    • React Redux 中文文档.
  • 了解react-redux的其他博客:
    • React-Redux 的历史和实现.
  • 关于下面用到的connect API,去官网去官网:
    https://cn.react-redux.js.org/tutorials/connect.

1.3 安装react-redux

  • 安装命令如下:
    # If you use npm:
    npm install react-redux
    
    # Or if you use Yarn:
    yarn add react-redux
    

2. 简单改写redux的例子

  • 注意,这个改写是在redux项目版本的基础上改写的,关于redux版本的,看下面的:
    React(react18)中组件通信04——redux入门.

2.1 提供store

  • 第一步我们需要使得 store 对于我们的应用是可见的。为了做到这个,我们使用 React Redux 提供的 API 去包裹我们的应用。:
    • 首先先给改写后的目录结构
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第1张图片
    • 然后再看app.js 和 index.js
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第2张图片

2.2 连接 Components + UI组件修改

2.2.1 连接 Components

  • 先看官网怎么讲解的
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第3张图片

  • 先简单写写实现效果,后续再优化,如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第4张图片

    import CountNumRedux from "../components/CountNumRedux";
    import { connect } from "react-redux";
    import store from '../redux/store'
    
    //这里ownProps如果用不到的话,可以不传,可以只传state
    const mapStateToProps = (state, ownProps) => ({
          // ...依据 state 和 自定义 ownProps 生成 computed data
          /**
           * 即状态统一在容器组件中管理
           * UI组件使用的话直接通过props取就行了,这种方式也相当于通过props传递
           * 如果监听state的变化,一有变化就调用,并把state通过props传递给UI组件
           */
          count:state
        //   name:'麦兜'
      });
    
      const mapDispatchToProps = ()=>({
        // ... 通常是一个充满 action creators 的 object
           addNumber:(number)=>{
               store.dispatch(
                   { type: 'INCREMENT', number:number }
               )
           },
           reduceNumber:(number)=>{
               store.dispatch(
                   { type: 'DECREMENT', number:number }
               )
           }
     });
    
      
    //   // 1. `connect` 返回一个接收要包装的组件的新函数:
    //   const connectToStore = connect(mapStateToProps, mapDispatchToProps);
    
    //   // 2. 并且该函数返回连接的,包装的组件:
    //   const ConnectedComponent = connectToStore(Component);
      
      // 通常我们会将两者一步完成,像这样:
    const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
    
    export default CountNumContainer;
    

2.2.2 修改UI组件

  • 如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第5张图片

    import {  createRef } from "react";
    // import store from '../redux/store'
    // import countAction from '../redux/countAction'
    
    function CountNumRedux(props){
        console.log(props);
    
        // const [count,setCount] = useState(0);
        const numberRef = createRef();
    
        function add(){
            let number = numberRef.current.value;
            // console.log(typeof number);  //string
            // store.dispatch(countAction.incrementNum(parseInt(number)));
            props.addNumber(parseInt(number));
        }
    
        function subtract(){
            let number = parseInt(numberRef.current.value);
            props.reduceNumber(number);
        }
    
        // useEffect(()=>{
        //     store.subscribe(()=>{
        //         console.log('订阅更新,打印2-----',store.getState());
        //         setCount(store.getState());
        //     });
        // });
    
        return(
            <div>
                {/* 当前数字是:{count}        
                当前数字是:{store.getState()}   */}
    
                当前数值是:{props.count}
                <br />
                浮动数字:<input type="number" ref={numberRef}/>
    
                <br /><br />
                <button onClick={add}>点我 加数</button> <br /><br />
                <button onClick={subtract}>点我 减数</button>
            </div>
        )
    }
    export default CountNumRedux;
    

2.2.3 看效果

  • 如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第6张图片

2.3 连接 Components——优化(优化容器组件)

  • 主要优化 mapDispatchToProps,用封装好的action,如下:

    import CountNumRedux from "../components/CountNumRedux";
    import { connect } from "react-redux";
    // import store from '../redux/store'
    import {incrementNum,decrementNum} from "../redux/countAction";
    
    
    const mapStateToProps = (state) => ({
          count:state
      });
    
    
    //   const mapDispatchToProps = ()=>({
    //        addNumber:(number)=>{
    //            store.dispatch(
    //                { type: 'INCREMENT', number:number }
    //            )
    //        },
    //        reduceNumber:(number)=>{
    //            store.dispatch(
    //                { type: 'DECREMENT', number:number }
    //            )
    //        }
    //  });
    
    /**
     * 1. dispatch:react-redux 会将dispatch传入,所以不用引入store来调了
     * 2. 引入已经封装好的action:countAction
     */
     const mapDispatchToProps = (dispatch)=>({
        addNumber:(number)=>{
            dispatch( incrementNum(number) )
        },
        reduceNumber:(number)=>{
            dispatch( decrementNum(number) )
        }
    });
    
    const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
    
    export default CountNumContainer;
    

2.4 优化容器组件(可怕的精简)

  • mapDispatchToProps: 此参数可以是一个 function,或者一个 object。
    • 上面都是用function写的,接下来换成object之后,代码真的太少了!
    • 不妨再看一下官方强调的:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第7张图片
  • 精简代码如下:
    /**
     * 优化2
     */
    const mapDispatchToProps = {
        //通常是一个充满 action creators 的 object
        addNumber: incrementNum,   //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
        reduceNumber: decrementNum
    }
    
    对比一下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第8张图片

2.5 附代码

  • 关于redux文件下的代码就不贴了,因为没改动,需要的直接上篇文章就行,其他如下:
    • CountNumContainer.jsx
      import CountNumRedux from "../components/CountNumRedux";
      import { connect } from "react-redux";
      import {incrementNum,decrementNum} from "../redux/countAction";
      
      const mapStateToProps = (state) => ({
            count:state
        });
      
      const mapDispatchToProps = {
          //通常是一个充满 action creators 的 object
          addNumber: incrementNum,   //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
          reduceNumber: decrementNum
      }
      
      const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
      
      export default CountNumContainer;
      
    • CountNumRedux.jsx
      import {  createRef } from "react";
      
      function CountNumRedux(props){
          console.log(props);
          
          const numberRef = createRef();
      
          function add(){
              let number = numberRef.current.value;
              props.addNumber(parseInt(number));
          }
      
          function subtract(){
              let number = parseInt(numberRef.current.value);
              props.reduceNumber(number);
          }
      
          return(
              <div>
                  {/* 当前数字是:{count}        
                  当前数字是:{store.getState()}   */}
      
                  当前数值是:{props.count}
                  <br />
                  浮动数字:<input type="number" ref={numberRef}/>
      
                  <br /><br />
                  <button onClick={add}>点我 加数</button> <br /><br />
                  <button onClick={subtract}>点我 减数</button>
              </div>
          )
      }
      export default CountNumRedux;
      
    • App.js
      import CountNumContainer from './container/CountNumContainer.jsx'
      
      function App() {
        return (
          <div>
            {/*  */}
            <CountNumContainer/>
          </div>
        );
      }
      
      export default App;
      
    • index.js
      import React from 'react';
      import ReactDOM from 'react-dom/client';
      import App from './App';
      
      import store from './redux/store';
      import { Provider } from 'react-redux';
      
      
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(
          <Provider store={store}>
              <App />
          </Provider>
      );
      
      export default root;
      

3. 多reducer实现数据共享

3.1 介绍 combineReducers()函数

  • 随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。

    combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

    合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名
    示例:

    rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
    // 这将返回如下的 state 对象
    {
      potato: {
        // ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ...
      },
      tomato: {
        // ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ...
      }
    }
    

    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第9张图片

  • 关于 combineReducers()函数 的介绍来源于官网,关于combineReducers()更多讲解,可去官网,如下:
    https://cn.redux.js.org/api/combinereducers.

3.2 多reducer整合的例子

3.2.1 想实现的效果

  • 先设计三个组件的渲染,如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第10张图片
  • 这三个组件里的状态都是交给react- redux管理的,我们先实现这个无数据共享的,然后再实现怎么让组件之间可以数据共享。

3.2.2 项目目录设计结构

  • 如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第11张图片

3.2.3 将3个reducer整合(或者说拆分reducer)

  • 关于reducer的拆分,可以去官网,上面介绍combineReducers()函数时也说了,这里就不多说了,也可以去官网看拆分reducer逻辑,地址如下:
    拆分 Reducer 逻辑.
  • 上面的cat、dog、pet分别对应1个reducer,但是创建store的时候只需要一个reducer,所以最终需要将这三个reducer函数合并成一个最终的reducer函数给创建store时使用。
  • 本项目中怎么使用 combineReducers() 的,你现在可以不用知道其他3个reducer长什么样,只要他们3个都暴露出了就行,所以我这里直接介绍合并,如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第12张图片

3.2.4 每个组件的三个核心文件

3.2.4.1 简单介绍——以cat为例
  • 这里为了看着方便,没有抽出常量,把UI组件和容器组件整合在了一个文件里,所以上面看到的3个模块组件各对应3个核心文件:action、reducer、容器组件。
  • 下面以cat组件为例进行说明:
    • catAction + catReducer:
      猫这里只想更改“今日最受欢迎的猫品种“,所以这个相对来说是简单的,一个action函数就可以了,那么如果action设计好了,reducer也就可以完善了,如下:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第13张图片
    • CatContainer 组件 ,如下:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第14张图片
3.2.4.2 关于dog 和 petPark 的
  • dog的,简单直接看吧,如下:
    • dogAction + dogReducer:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第15张图片
    • DogContainer 组件 ,如下:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第16张图片
  • petPark的如下:
    这个比较简单点,因为这个里没有设计状态的改变,所以没有对应的action,都是初试值,如下:
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第17张图片

3.3 实现数据共享

3.3.1 实现数据共享

  • 现在在上面效果的基础上,实现数据共享,就很简单了,加两行代码的事,跟取自己的一样,如下:
    • petPark访问其他两个组件的数据:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第18张图片
    • cat访问petPark的数据,也是一样的,想怎么访问怎么访问,因为本来就不在组件内部管理,而是react-redux在管理,谁用谁取就是了:
      React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第19张图片

3.4 附核心代码

3.4.1 两个action

  • catAction 如下:

    
    function changeCatKindAction(newKind){
        return {
            type: 'CHANGE_CAT_KIND',
            kind: newKind
        }
    }
    
    export {changeCatKindAction}
    
  • dogAction 如下:

    
    function addDogAction(dogObj){
        return {
            type:'ADD_DOG',
            dog:dogObj
        }
    }
    
    export {addDogAction}
    

3.4.2 三个reducer + 一个整合reducer

  • 前三个如下:

    const catKindInit = '布偶';
    
    function catReducer(state=catKindInit, action){
        switch (action.type) {
            case 'CHANGE_CAT_KIND':
                return action.kind;
            default:
                return state;
        }
    }
    
    export default catReducer;
    
    
    const dogInit = [];
    // const dogInit = [{dogName:'狗狗',dogAge:1}];
    
    function dogReducer(state = dogInit, action){
        const {type,dog} = action;
        switch (type) {
            case 'ADD_DOG':
                return [...state,dog];
            default:
                return state;
        }
    }
    
    export default dogReducer;
    
    
    const adminInit = {parkAdmin:'素素',parkAdminPhone:'176XXXXX'};
    
    function PetParkReducer(state=adminInit, action){
        return state;  //没有action,初始state不需要修改,直接返回
    }
    
    export default PetParkReducer;
    
  • 最终的如下:

    
    import catReducer from "./catsReducer";
    import dogReducer from "./dogReducer";
    import petsParkReducer from "./petsParkReducer";
    
    import { combineReducers } from "redux";
    
    /**
     * 1. 合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 
     * 2. 由 combineReducers() 返回的 state 对象,
     *    会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
     */
    const rootReducer = combineReducers({
        petParkState: petsParkReducer,
        dogState: dogReducer,
        catState: catReducer,
    });
    
    export default rootReducer;
    

3.4.3 三个组件

  • CatContainer.jsx 如下:

    import { connect } from "react-redux";
    import { useRef } from "react";
    import {changeCatKindAction} from '../redux/actions/CatAction'
    
    //1. UI组件
    function CatUI(props){
        const catKindNode = useRef();
    
        function chagePopularKind(){
            const newKind = catKindNode.current.value;
            props.changKind(newKind);
        }
    
        return(
            <div>
                <h1>我是cat组件</h1>
                今日最受欢迎的小猫品种是:{props.popularCatKind} <br/><br/>
    
                <input type="text" ref={catKindNode} placeholder="请输入今日最受欢迎的"/> &nbsp;
                <button onClick={chagePopularKind}>修改最受欢迎的小猫品种</button>
    
                <br />
                今日管理员是:{props.guanliyuan}  <br/>
                管理员:{props.guanliyuan2.parkAdmin} 
            </div>
        )
    }
    
    //2. 容器组件
    
    function mapStateToProps(state) {
        return {
            popularCatKind: state.catState,
    
            guanliyuan: state.petParkState.parkAdmin, //可以直接访问其中某个属性
            guanliyuan2: state.petParkState, //也可以直接访问整个对象
        }
    }
    
    const mapDispatchToProps = {
        changKind: changeCatKindAction
    }
    
    const CatContainer = connect(mapStateToProps,mapDispatchToProps)(CatUI);
    
    export default CatContainer;
    
  • DogContainer.jsx 如下:

    import { useRef } from "react";
    import { connect } from "react-redux"
    import { addDogAction } from "../redux/actions/DogAction";
    
    //1. 定义UI组件
    function DogUI(props){
        // console.log(props);
        const dogList = props.dogListState;//获取狗狗列表信息
    
        const dogNameRef = useRef();
        const dogAgeRef = useRef();
    
        function addParkDog(){
            const dogName = dogNameRef.current.value;
            const dogAge = dogAgeRef.current.value;
            const dogObj = {dogName:dogName,dogAge:dogAge}
            props.addOneDog(dogObj);
        }
    
        return(
            <div>
                <h1>我是dog组件</h1> <br />
                1. 狗狗园区地址:{props.dogParkAdress}    <br /><br />
                2. 
                狗狗姓名:<input type="text" ref={dogNameRef} />    <br /> &nbsp;&nbsp;&nbsp;
                狗狗年龄:<input type="number" ref={dogAgeRef}/>  &nbsp;
                <button onClick={addParkDog}>添加狗狗</button> <br /><br />
                3. 狗狗列表信息:
                <ul>
                    {
                        dogList.map((dog,index)=>(
                            <li key={index}>{dog.dogName}---{dog.dogAge}</li>)
                        )
                    }
                </ul>
            </div>
            
        )
    }
    
    //2.容器组件 并导出容器组件
    
    const mapStateToProps = (state)=>{
        /**
         * 1. 返回的是一个对象(dog组件 管理自己组件的state)
         * 2. 语法问题:当返回的是一个对象时,用一对()括起来,否则语法报错
         */
        return(
            {
                dogListState:state.dogState,
                dogParkAdress:'北京海淀区'
            }
        )
    }
    
    const mapDispatchToProps = {
        addOneDog: addDogAction
    }
    
    const DogContainer = connect(mapStateToProps,mapDispatchToProps)(DogUI);
    
    export default DogContainer;
    
  • PetParkContainer.jsx 如下:

    import { connect } from "react-redux";
    import { useState } from "react";
    
    //1. UI组件
    function PetParkUI(props){
        console.log(props);
        const [closeFlag,setCloseFlag] = useState(false);
    
        console.log(closeFlag);
        return(
            <div>
                <h1>我是PetPark组件</h1> 
                1. 管理员:{props.parkAdminInfo.parkAdmin}  <br /> &nbsp;&nbsp;
                   管理员电话:{props.parkAdminInfo.parkAdminPhone}  <br />
                2. 现有的宠物有:{JSON.stringify(props.petKinds)} <br />
                3. 雨天是否闭园:{closeFlag ? '是' : '否'}  <br /><br />
    
                今日猫猫种类王是:{props.catKindKing}  <br /><br />
                今日dog园区有多少条狗狗:{props.dogsNum}
            </div>
        )
    }
    
    //2.容器组件
    const mapStateToProps = (state)=>({
        parkAdminInfo: state.petParkState,//这个交给react-redux管理的可以共享
        petKinds: ['猫猫','狗狗'] ,//这个如果是自身组件用的,可以用useState放自身组件上
    
        //下面是数据共享的
        catKindKing: state.catState,  //直接取cat组件里的状态
        dogsNum: state.dogState.length
    
    })
    
    //connect 的两个参数都是可选的,可传可不传
    const PetParkContainer = connect(mapStateToProps)(PetParkUI);
    
    export default PetParkContainer;
    

4. 附项目

  • 项目目录
    React(react18)中组件通信05——redux ➕ react-redux(含数据共享)_第20张图片
  • 下载项目
    react(react18)组件通信的各种方式以及各种详细例子(含react-redux + redux-toolkit).

你可能感兴趣的:(react,react.js,前端,前端框架,react-redux,react状态管理,react组件通信,react-redux数据共享)