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

1. 前言

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

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 提供的 <Provider /> 组件,这使得 Redux store 能够在应用的其他地方使用(即:store只需在入口文件传递一次,其他需要store的容器组件中都可以获取)。

1.2.2 官网

1.3 安装react-redux

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

2. 简单改写redux的例子

2.1 提供store

  • 第一步我们需要使得 store 对于我们的应用是可见的。为了做到这个,我们使用 React Redux 提供的 API <Provider /> 去包裹我们的应用。:
    • 首先先给改写后的目录结构
      在这里插入图片描述
    • 然后再看app.js 和 index.js
      在这里插入图片描述

2.2 连接 Components + UI组件修改

2.2.1 连接 Components

  • 先看官网怎么讲解的
    在这里插入图片描述

  • 先简单写写实现效果,后续再优化,如下:
    在这里插入图片描述

    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组件

  • 如下:
    在这里插入图片描述

    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}    &nbsp;&nbsp;&nbsp;&nbsp;
                当前数字是:{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 看效果

  • 如下:
    在这里插入图片描述

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之后,代码真的太少了!
    • 不妨再看一下官方强调的:
      在这里插入图片描述
  • 精简代码如下:
    /**
     * 优化2
     */
    const mapDispatchToProps = {
          
          
        //通常是一个充满 action creators 的 object
        addNumber: incrementNum,   //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
        reduceNumber: decrementNum
    }
    
    对比一下:
    在这里插入图片描述

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}    &nbsp;&nbsp;&nbsp;&nbsp;
                  当前数字是:{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>
            {
              
              /* <CountNumRedux/> */}
            <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 属性 ...
      }
    }
    

    在这里插入图片描述

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

3.2 多reducer整合的例子

3.2.1 想实现的效果

  • 先设计三个组件的渲染,如下:
    在这里插入图片描述
  • 这三个组件里的状态都是交给react- redux管理的,我们先实现这个无数据共享的,然后再实现怎么让组件之间可以数据共享。

3.2.2 项目目录设计结构

  • 如下:
    在这里插入图片描述

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

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

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

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

3.3 实现数据共享

3.3.1 实现数据共享

  • 现在在上面效果的基础上,实现数据共享,就很简单了,加两行代码的事,跟取自己的一样,如下:
    • petPark访问其他两个组件的数据:
      在这里插入图片描述
    • cat访问petPark的数据,也是一样的,想怎么访问怎么访问,因为本来就不在组件内部管理,而是react-redux在管理,谁用谁取就是了:
      在这里插入图片描述

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. 附项目

猜你喜欢

转载自blog.csdn.net/suixinfeixiangfei/article/details/133129625
今日推荐