React でのコンポーネント通信 (react18) 05 - redux ➕ act-redux (データ共有を含む)

1 はじめに

1.1 React におけるコンポーネント通信のその他の方法

1.2 React-Redux の概要

1.2.1 React-Redux の簡単な紹介

  • React-Redux は、Redux が公式に提供する React バインディング ライブラリです。効率的かつ柔軟。
    • React-redux は、React アプリケーション開発用に設計された Redux ベースのライブラリであり、React 固有の機能とコンポーネントをいくつか提供します。
    • React コンポーネントでの状態管理のための Redux の使用を容易にする一連の API とコンポーネントを提供します。
  • React-Redux は概念的には非常にシンプルです。Redux ストアをサブスクライブし、コンポーネントに必要なデータが変更されたかどうかを確認します。コンポーネントを再レンダリングします
  • React-redux は、Connect 関数や Provider コンポーネントなど、React 固有の関数をいくつか提供します。これらの関数は、Redux ストアに接続し、状態を React コンポーネントに渡すために使用されます。
    • React Redux によって提供されるため<Provider /> 组件、Redux ストアをアプリケーション内の他の場所で使用できるようになります (つまり、ストアはエントリ ファイルで 1 回渡すだけでよく、ストアを必要とする他のコンテナ コンポーネントで取得できます)。

1.2.2 公式ウェブサイト

1.3 リアクトリダックスをインストールする

  • インストールコマンドは以下のとおりです。
    # If you use npm:
    npm install react-redux
    
    # Or if you use Yarn:
    yarn add react-redux
    

2. reduxを単純に書き換える例

2.1 ストアの提供

  • 最初のステップは、ストアをアプリケーションで認識できるようにすることです。これを行うには、React Redux が提供する API を使用して<Provider />アプリケーションをラップします。:
    • まず、書き換えられたディレクトリ構造を与えます
      ここに画像の説明を挿入します
    • 次に、app.js を見て、インデックス.js
      ここに画像の説明を挿入します

2.2 Connect Components + UIコンポーネントの変更

2.2.1 コンポーネントの接続

  • まずは公式サイトの説明を見てみましょう
    ここに画像の説明を挿入します

  • 次のように、最初に実装効果を簡単に書き留めてから、後で最適化しましょう。
    ここに画像の説明を挿入します

    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 コンポーネントの接続 - 最適化(コンテナコンポーネントの最適化)

  • 主に次のように、mapDispatchToProps を最適化し、カプセル化されたアクションを使用します。

    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: このパラメータには関数またはオブジェクトを指定できます。
    • 上記はすべて関数で記述されていますが、オブジェクトに変更すると、コードが本当に少なすぎます。
    • 当局者が強調したことをもう一度見てみましょう。
      ここに画像の説明を挿入します
  • 簡略化されたコードは次のとおりです。
    /**
     * 优化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;
      
    • インデックス.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 combinReducers() 関数の概要

  • アプリケーションがより複雑になるにつれて、次の点を考慮してください。レデューサー関数を個別の関数に分割する、分割後の各関数は、状態の一部を独立して管理する責任を負います。

    combinReducers 補助関数は、複数の異なるリデューサー関数を持つオブジェクトを値として最終的なリデューサー関数に結合し、このリデューサーで createStore メソッドを呼び出します。

    マージされたリデューサーは、各サブリデューサーを呼び出し、返された結果を状態オブジェクトにマージできます。comboReducers() によって返される状態オブジェクトは、combineReducers() に渡されるときに、対応するキーに従って、各受信リデューサーによって返される状態に名前を付けます。
    例:

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

    ここに画像の説明を挿入します

  • 関数の紹介combineReducers()は公式 Web サイトからのもので、combineReducers()詳細については、次の公式 Web サイトを参照してください:
    https://cn.redux.js.org/api/combinereducers

3.2 複数の減速機の統合例

3.2.1 達成したい効果

  • まず、次のように 3 つのコンポーネントのレンダリングを設計します。
    ここに画像の説明を挿入します
  • これら 3 つのコンポーネントの状態はすべて React-redux によって管理されており、最初にデータ共有なしでこれを実装し、次にコンポーネント間でデータ共有を有効にする方法を実装します。

3.2.2 プロジェクトディレクトリの設計構造

  • 次のように:
    ここに画像の説明を挿入します

3.2.3 3 つの減速機を統合する (または分割減速機)

  • レデューサーの分割については、公式サイトにアクセスできます。上記の機能を紹介するcombineReducers()ときに言及したので、ここでは詳細は説明しません。また、公式ウェブサイトにアクセスして、分割リデューサーのロジックを確認することもできます。アドレスは次のとおりです:
    Split Reducer Logic
  • 上記の cat、dog、pet はそれぞれ 1 つのレデューサーに対応しますが、ストアを作成するときに必要なレデューサーは 1 つだけなので、最終的にはこれら 3 つのレデューサー関数が必要になります。最終リデューサー関数に結合ストアを作成するときに使用します。
  • このプロジェクトでの使用方法combineReducers()、3 つすべてが公開されている限り、他の 3 つのレデューサーがどのように見えるかを知る必要はありません。そのため、次のようにここで直接マージを紹介します。
    ここに画像の説明を挿入します

3.2.4 各コンポーネントの 3 つのコア ファイル

3.2.4.1 簡単な紹介 - 猫を例に取る
  • ここでは便宜上、定数は抽出せず、UIコンポーネントとコンテナコンポーネントを1つのファイルに統合しているため、上記の3つのモジュールコンポーネントは、アクション、リデューサ、コンテナコンポーネントの3つのコアファイルにそれぞれ対応しています。
  • 以下では、例として cat コンポーネントを使用して説明します。
    • catAction + catReducer:
      ここの猫は、「今日最も人気のある猫の品種」を変更したいだけなので、これは比較的単純で、単なるアクション関数です。アクションが適切に設計されていれば、次のようにリデューサーも完成させることができます。
      ここに画像の説明を挿入します
    • 猫コンテナコンポーネントは次のとおりです。
      ここに画像の説明を挿入します
3.2.4.2 犬とペットパークについて
  • 犬の場合は、次のように直接見てください。
    • ドッグアクション + ドッグリデューサー:
      ここに画像の説明を挿入します
    • 犬コンテナコンポーネントは次のとおりです。
      ここに画像の説明を挿入します
  • petPark の値は次のとおりです:
    これは比較的単純です。設計ステータスに変更がないため、対応するアクションはありません。これらはすべて次のような暫定値です。
    ここに画像の説明を挿入します

3.3 データ共有の実現

3.3.1 データ共有の実現

  • 上記の効果に基づいて、データ共有を実現するのは非常に簡単です。次のように、2 行のコードを追加することは、独自のコードを追加するのと同じです。
    • petPark は、他の 2 つのコンポーネントからのデータにアクセスします。
      ここに画像の説明を挿入します
    • Cat は同じ方法で petPark データにアクセスします。コンポーネント内で管理されないため、必要に応じてアクセスできますが、react-redux が管理します。使用する人は誰でも取得できます。
      ここに画像の説明を挿入します

3.4 付属コアコード

3.4.1 2 つのアクション

  • 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 3 つの減速機 + 1 つの統合減速機

  • 最初の 3 つは次のとおりです。

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