Component communication in React (react18) 05 - redux ➕ react-redux (including data sharing)

1 Introduction

1.1 Other ways of component communication in React

1.2 Introduction to React-Redux

1.2.1 A brief introduction to React-Redux

  • React-Redux is the React binding library officially provided by Redux. Efficient and flexible.
    • react-redux is a Redux-based library designed for React application development, providing some React-specific functions and components.
    • It provides a series of APIs and components to facilitate the use of Redux for state management in React components.
  • React-Redux is conceptually very simple. It subscribes to the Redux store, checks if the data required by the component has changed,and re-render the component
  • react-redux provides some React-specific functions, such as the connect function and Provider component, which are used to connect to the Redux store and pass state to React components.
    • Provided by React Redux <Provider /> 组件, this enables the Redux store to be used elsewhere in the application (i.e. the store only needs to be passed once in the entry file and can be obtained in other container components that require the store).

1.2.2 Official website

1.3 Install react-redux

  • The installation command is as follows:
    # If you use npm:
    npm install react-redux
    
    # Or if you use Yarn:
    yarn add react-redux
    

2. Example of simply rewriting redux

2.1 Provide store

  • The first step is to make the store visible to our application. To do this, we use the API provided by React Redux <Provider />to wrap our application. :
    • First, give the rewritten directory structure
      Insert image description here
    • Then look at app.js andindex.js
      Insert image description here

2.2 Connect Components + UI component modification

2.2.1 Connect Components

  • Let’s first look at how the official website explains it
    Insert image description here

  • Let’s simply write down the implementation effect first, and then optimize it later, as follows:
    Insert image description here

    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 Modify UI components

  • as follows:
    Insert image description here

    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 See the effect

  • as follows:
    Insert image description here

2.3 Connecting Components - Optimization (optimizing container components)

  • Mainly optimize mapDispatchToProps and use encapsulated actions, as follows:

    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 Optimizing container components (horrible simplification)

  • mapDispatchToProps: This parameter can be a function or an object.
    • The above are all written in function. After changing to object, the code is really too little!
    • Let’s take another look at what the official emphasized:
      Insert image description here
  • The simplified code is as follows:
    /**
     * 优化2
     */
    const mapDispatchToProps = {
          
          
        //通常是一个充满 action creators 的 object
        addNumber: incrementNum,   //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
        reduceNumber: decrementNum
    }
    
    Compare:
    Insert image description here

2.5 Attached code

  • I won’t post the code under the redux file because there are no changes. If you need it, just go to the previous article. The others are as follows:
    • 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. Multiple reducers realize data sharing

3.1 Introduction to combineReducers() function

  • As applications become more complex, considerSplit the reducer function into separate functions, each function after splitting is responsible for independently managing part of the state.

    The combineReducers auxiliary function is to combine an object with multiple different reducer functions as value into a final reducer function , and then call the createStore method on this reducer.

    The merged reducer can call each sub-reducer and merge their returned results into a state object.The state object returned by combineReducers() will name the state returned by each incoming reducer according to the corresponding key when it is passed to combineReducers()..
    Example:

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

    Insert image description here

  • The introduction combineReducers()to functions comes from the official website. For combineReducers()more explanation, you can go to the official website, as follows:
    https://cn.redux.js.org/api/combinereducers .

3.2 Example of multi-reducer integration

3.2.1 The effect you want to achieve

  • First design the rendering of three components, as follows:
    Insert image description here
  • The states in these three components are all managed by react-redux. We first implement this without data sharing, and then implement how to enable data sharing between components.

3.2.2 Project directory design structure

  • as follows:
    Insert image description here

3.2.3 Integrate three reducers (or split reducers)

  • Regarding the splitting of the reducer, you can go to the official website. combineReducers()I mentioned it when introducing the function above, so I won’t go into details here. You can also go to the official website to see the splitting reducer logic. The address is as follows:
    Split Reducer Logic .
  • The above cat, dog, and pet correspond to one reducer respectively, but only one reducer is needed when creating the store, so in the end, these three reducer functions need to beCombined into a final reducer functionUsed when creating a store.
  • How to use in this project combineReducers(), you don’t need to know what the other three reducers look like now, as long as all three of them are exposed, so I will directly introduce the merger here, as follows:
    Insert image description here

3.2.4 Three core files for each component

3.2.4.1 Brief introduction - taking cat as an example
  • For the sake of convenience, the constants are not extracted here, and the UI components and container components are integrated into one file. Therefore, the three module components seen above each correspond to three core files: action, reducer, and container component.
  • The following uses the cat component as an example to illustrate:
    • catAction + catReducer:
      Cat here only wants to change "Today's most popular cat breeds", so this is relatively simple, just an action function, then if the action is designed well, the reducer can also be perfected, as follows:
      Insert image description here
    • CatContainerComponents, as follows:
      Insert image description here
3.2.4.2 About dog and petPark
  • For dog, just look at it directly, as follows:
    • dogAction + dogReducer:
      Insert image description here
    • DogContainerComponents, as follows:
      Insert image description here
  • petPark's is as follows:
    This one is relatively simple, because there is no change in design status, so there is no corresponding action. They are all preliminary values, as follows:
    Insert image description here

3.3 Realize data sharing

3.3.1 Realize data sharing

  • Now based on the above effect, it is very simple to realize data sharing. Adding two lines of code is the same as taking your own, as follows:
    • petPark accesses data from the other two components:
      Insert image description here
    • Cat accesses the petPark data in the same way. You can access it however you want, because it is not managed inside the component, but react-redux manages it. Whoever uses it can get it:
      Insert image description here

3.4 Attached core code

3.4.1 Two actions

  • catAction is as follows:

    
    function changeCatKindAction(newKind){
          
          
        return {
          
          
            type: 'CHANGE_CAT_KIND',
            kind: newKind
        }
    }
    
    export {
          
          changeCatKindAction}
    
  • dogAction is as follows:

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

3.4.2 Three reducers + one integrated reducer

  • The first three are as follows:

    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;
    
  • The final one is as follows:

    
    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 Three components

  • CatContainer.jsxas follows:

    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.jsxas follows:

    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.jsxas follows:

    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. Attached items

Guess you like

Origin blog.csdn.net/suixinfeixiangfei/article/details/133129625