検索
ホームページウェブフロントエンドjsチュートリアルReact コンポーネント間で通信する 10 の方法の詳細な分析

React コンポーネント間で通信する 10 の方法の詳細な分析

Feb 25, 2021 am 10:10 AM
reactコンポーネントコンポーネント通信

React コンポーネント間で通信する 10 の方法の詳細な分析

[関連チュートリアルの推奨事項: React ビデオ チュートリアル ]

以前、小規模プロジェクトに取り組むために別のプロジェクト チームに一時的に異動しました。 2 日間繰り返します。このプロジェクトのフロントエンドは React を使用していますが、小規模なプロジェクトなので Redux などの状態管理ライブラリは使用していません。関係のない 2 つのコンポーネント間で通信する方法という小さな問題が発生しました。この質問は非常に興味深いと思うので、みんなで議論できるように私の思考プロセスを書き留めておきます。

関連のない 2 つのコンポーネント間の通信に焦点を当てていますが、誰もが過去から学べるように、親コンポーネントと子コンポーネント間の最も一般的な通信から始めます。最初に完全な概要をリストし、次に詳細を展開します。

コンポーネント間の通信メソッドの概要

  • ##親コンポーネント=>子コンポーネント:

      Props
    1. インスタンス メソッド
  • ##子コンポーネント=>親コンポーネント:
  • コールバック関数
    1. イベント バブリング
  • 兄弟コンポーネント間:
  • 親コンポーネント
    #関連性の低いコンポーネント間:
  • コンテキスト

      ポータル
    1. グローバル変数
    2. オブザーバーパターン
    3. Redux etc
    4. # #1. 小道具
  • これは、反応コンポーネント間で情報を転送する最も一般的な方法です。親コンポーネントは props を通じて子コンポーネントにデータを渡し、子コンポーネントは
this.props

を渡します。対応するデータを使用します

const Child = ({ name }) => {
    <div>{name}</div>
}

class Parent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: &#39;zach&#39;
        }
    }
    render() {
        return (
            <Child name={this.state.name} />
        )
    }
}

2. インスタンス メソッド親コンポーネントから子コンポーネントに情報を渡す 2 番目の方法は、一部の学生には馴染みがないかもしれませんが、この方法は非常に便利なので、必ずマスターしてください。 . .原理: 親コンポーネントは、

refs

を使用して子コンポーネント インスタンスのメソッドを直接呼び出すことができます。次の例を参照してください:

class Child extends React.Component {
  myFunc() {
    return "hello"
  }
}

class Parent extends React.Component {
  componentDidMount() {
    var x = this.foo.myFunc()   // x is now &#39;hello&#39;
  }
  render() {
    return (
      <Child
        ref={foo => {
          this.foo = foo
        }}
      />
    )
  }
}

一般的なプロセス:

最初に、子コンポーネントには myFunc メソッドがあります。

親コンポーネントは、
    callback-refs
  1. の形式で ref 属性を子コンポーネントに渡します。このコールバック関数は、反応コンポーネント インスタンス/ネイティブ DOM 要素をパラメーターとして受け取ります。親コンポーネントがマウントされると、react は ref コールバック関数を実行し、子コンポーネントのインスタンスをパラメータとしてコールバック関数に渡します。その後、子コンポーネントのインスタンスを
  2. this.foo
  3. に割り当てます。 最後に、親コンポーネントで this.foo を使用して、サブコンポーネントのメソッドを呼び出すことができます。
  4. このメソッドの原理を理解した後、次のことを行う必要があります。考慮すべき問題は、なぜこの方法を使用するのか、またその使用シナリオは何なのかということです。最も一般的な使用シナリオ: たとえば、サブコンポーネントはモーダル ポップアップ コンポーネントです。サブコンポーネントには、モーダル ポップアップ ウィンドウを表示/非表示にするさまざまなメソッドがあります。このメソッドを使用して、親コンポーネント上でサブコンポーネントを直接呼び出すことができます。これらのメソッドインスタンスの は、子コンポーネントの表示/非表示を制御するために使用されます。このメソッドは、モーダルの表示/非表示を制御する props を子コンポーネントに渡すよりもはるかに美しいです。
    class Modal extends React.Component {
      show = () => {// do something to show the modal}
      hide = () => {// do something to hide the modal}
      render() {
        return <div>I&#39;m a modal</div>
      }
    }
    
    class Parent extends React.Component {
      componentDidMount() {
        if(// some condition) {
            this.modal.show()
        }
      }
      render() {
        return (
          <Modal
            ref={el => {
              this.modal = el
            }}
          />
        )
      }
    }
  5. 3. コールバック関数

親コンポーネントが子コンポーネントに情報を送信する 2 つの方法について説明した後、子コンポーネントが親コンポーネントに情報を送信する方法について説明します。コールバック関数メソッドも最も一般的な反応方法であり、子コンポーネントは、親コンポーネントから渡されたコールバック関数を呼び出すことによって、親コンポーネントにデータを渡します。

const Child = ({ onClick }) => {
    <div onClick={() => onClick(&#39;zach&#39;)}>Click Me</div>
}

class Parent extends React.Component {
    handleClick = (data) => {
        console.log("Parent received value from child: " + data)
    }
    render() {
        return (
            <Child onClick={this.handleClick} />
        )
    }
}

4. イベント バブリング

このメソッドは実際には React 自体とは何の関係もなく、ネイティブ dom 要素のイベント バブリング メカニズムを使用します。

class Parent extends React.Component {
  render() {
    return (
      <div onClick={this.handleClick}>
         <Child />
      </div>
    );
  }
  handleClick = () => {
    console.log(&#39;clicked&#39;)
  }
}
function Child {
  return (
    <button>Click</button>
  );    
}

イベント バブリング メカニズムをうまく利用すると、親コンポーネント要素上の子コンポーネント要素からクリック イベントを簡単に受け取ることができます

5. 親コンポーネント

についての説明を終えて親コンポーネントと子コンポーネント間の通信については、親以外のコンポーネントと子コンポーネント間の通信メソッドを見てみましょう。一般に、親子ではない 2 つのコンポーネントが通信したい場合、まずそれらが兄弟コンポーネントであるかどうか、つまり同じ親コンポーネントの下にあるかどうかを確認できます。そうでない場合は、それらをコンポーネントにラップして兄弟にすることが適切かどうかを検討してください。このようにして、中間層としての親コンポーネントを介したデータ通信を実現できます。

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {count: 0}
  }
  setCount = () => {
    this.setState({count: this.state.count + 1})
  }
  render() {
    return (
      <div>
        <SiblingA
          count={this.state.count}
        />
        <SiblingB
          onClick={this.setCount}
        />
      </div>
    );
  }

}

6. コンテキスト

通常、フロントエンド アプリケーションには、現在ログインしているユーザー情報、UI テーマ、ユーザーが選択した言語などの「グローバル」データが含まれます。これらのグローバル データは多くのコンポーネントで使用される可能性があります。コンポーネント レベルが非常に深い場合、以前の方法を使用すると、props を通じてレイヤーごとにデータを渡す必要があります。これは明らかに面倒です。次の例を参照してください:

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

上記の例では、Button 要素のテーマの色を取得するには、アプリからツールバーにテーマを小道具として渡し、次にツールバーから ThemedButton に渡し、最後に Button が親の小道具からテーマを取得する必要があります。コンポーネント ThemedButton.テーマ テーマ。異なるコンポーネントでButtonを使用する場合、この例のように随所にテーマを渡さなければならず、非常に面倒です。

したがって、react は新しい API を提供します: Context。Context を使用して上記の例を書き直します。

const ThemeContext = React.createContext(&#39;light&#39;);

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

簡単な分析:

  1. React.createContext创建了一个Context对象,假如某个组件订阅了这个对象,当react去渲染这个组件时,会从离这个组件最近的一个Provider组件中读取当前的context值
  2. Context.Provider: 每一个Context对象都有一个Provider属性,这个属性是一个react组件。在Provider组件以内的所有组件都可以通过它订阅context值的变动。具体来说,Provider组件有一个叫value的prop传递给所有内部组件,每当value的值发生变化时,Provider内部的组件都会根据新value值重新渲染
  3. 那内部的组件该怎么使用这个context对象里的东西呢?
    a. 假如内部组件是用class声明的有状态组件:我们可以把Context对象赋值给这个类的属性contextType,如上面所示的ThemedButton组件

        class ThemedButton extends React.Component {
          static contextType = ThemeContext;
          render() {
            const value = this.context
            return <Button theme={value} />;
          }
        }

    b. 假如内部组件是用function创建的无状态组件:我们可以使用Context.Consumer,这也是Context对象直接提供给我们的组件,这个组件接受一个函数作为自己的child,这个函数的入参就是context的value,并返回一个react组件。可以将上面的ThemedButton改写下:

        function ThemedButton {
            return (
                <ThemeContext.Consumer>
                    {value => <Button theme={value} />}
                </ThemeContext.Consumer>
            )
        }

最后提一句,context对于解决react组件层级很深的props传递很有效,但也不应该被滥用。只有像theme、language等这种全局属性(很多组件都有可能依赖它们)时,才考虑用context。如果只是单纯为了解决层级很深的props传递,可以直接用component composition

7. Portals

Portals也是react提供的新特性,虽然它并不是用来解决组件通信问题的,但因为它也涉及到了组件通信的问题,所以我也把它列在我们的十种方法里面。

Portals的主要应用场景是:当两个组件在react项目中是父子组件的关系,但在HTML DOM里并不想是父子元素的关系。

举个例子,有一个父组件Parent,它里面包含了一个子组件Tooltip,虽然在react层级上它们是父子关系,但我们希望子组件Tooltip渲染的元素在DOM中直接挂载在body节点里,而不是挂载在父组件的元素里。这样就可以避免父组件的一些样式(如overflow:hiddenz-indexposition等)导致子组件无法渲染成我们想要的样式。

如下图所示,父组件是这个红色框的范围,并且设置了overflow:hidden,这时候我们的Tooltip元素超出了红色框的范围就被截断了。

React コンポーネント間で通信する 10 の方法の詳細な分析

怎么用portals解决呢?

首先,修改html文件,给portals增加一个节点

<html>
    <body>
        <div id="react-root"></div>
        <div id="portal-root"></div>
    </body>
</html>

然后我们创建一个可复用的portal容器,这里使用了react hooks的语法,看不懂的先过去看下我另外一篇讲解react hooks的文章:30分钟精通React今年最劲爆的新特性——React Hooks

import { useEffect } from "react";
import { createPortal } from "react-dom";

const Portal = ({children}) => {
  const mount = document.getElementById("portal-root");
  const el = document.createElement("div");

  useEffect(() => {
    mount.appendChild(el);
    return () => mount.removeChild(el);
  }, [el, mount]);

  return createPortal(children, el)
};

export default Portal;

最后在父组件中使用我们的portal容器组件,并将Tooltip作为children传给portal容器组件

const Parent = () => {
  const [coords, setCoords] = useState({});

  return <div style={{overflow: "hidden"}}>
      <Button>
        Hover me
      </Button>
      <Portal>
        <Tooltip coords={coords}>
          Awesome content that is never cut off by its parent container!
         </Tooltip>
      </Portal>
  </div>
}

这样就ok啦,虽然父组件仍然是overflow: hidden,但我们的Tooltip再也不会被截断了,因为它直接超脱了,它渲染到body节点下的<div id="portal-root"></div>里去了。

总结下适用的场景: Tooltip、Modal、Popup、Dropdown等等

8. Global Variables

哈哈,这也不失为一个可行的办法啊。当然你最好别用这种方法。

class ComponentA extends React.Component {
    handleClick = () => window.a = &#39;test&#39;
    ...
}
class ComponentB extends React.Component {
    render() {
        return <div>{window.a}</div>
    }
}

9. Observer Pattern

观察者模式是软件设计模式里很常见的一种,它提供了一个订阅模型,假如一个对象订阅了某个事件,当那个事件发生的时候,这个对象将收到通知。

这种模式对于我们前端开发者来说是最不陌生的了,因为我们经常会给某些元素添加绑定事件,会写很多的event handlers,比如给某个元素添加一个点击的响应事件elm.addEventListener('click', handleClickEvent),每当elm元素被点击时,这个点击事件会通知elm元素,然后我们的回调函数handleClickEvent会被执行。这个过程其实就是一个观察者模式的实现过程。

那这种模式跟我们讨论的react组件通信有什么关系呢?当我们有两个完全不相关的组件想要通信时,就可以利用这种模式,其中一个组件负责订阅某个消息,而另一个元素则负责发送这个消息。javascript提供了现成的api来发送自定义事件: CustomEvent,我们可以直接利用起来。

首先,在ComponentA中,我们负责接受这个自定义事件:

class ComponentA extends React.Component {
    componentDidMount() {
        document.addEventListener(&#39;myEvent&#39;, this.handleEvent)
    }
    componentWillUnmount() {
        document.removeEventListener(&#39;myEvent&#39;, this.handleEvent)
    }
    
    handleEvent = (e) => {
        console.log(e.detail.log)  //i&#39;m zach
    }
}

然后,ComponentB中,负责在合适的时候发送该自定义事件:

class ComponentB extends React.Component {
    sendEvent = () => {
        document.dispatchEvent(new CustomEvent(&#39;myEvent&#39;, {
          detail: {
             log: "i&#39;m zach"
          }
        }))
    }
    
    render() {
        return <button onClick={this.sendEvent}>Send</button>
    }
}

这样我们就用观察者模式实现了两个不相关组件之间的通信。当然现在的实现有个小问题,我们的事件都绑定在了document上,这样实现起来方便,但很容易导致一些冲突的出现,所以我们可以小小的改良下,独立一个小模块EventBus专门这件事:

class EventBus {
    constructor() {
        this.bus = document.createElement(&#39;fakeelement&#39;);
    }

    addEventListener(event, callback) {
        this.bus.addEventListener(event, callback);
    }

    removeEventListener(event, callback) {
        this.bus.removeEventListener(event, callback);
    }

    dispatchEvent(event, detail = {}){
        this.bus.dispatchEvent(new CustomEvent(event, { detail }));
    }
}

export default new EventBus

然后我们就可以愉快的使用它了,这样就避免了把所有事件都绑定在document上的问题:

import EventBus from &#39;./EventBus&#39;
class ComponentA extends React.Component {
    componentDidMount() {
        EventBus.addEventListener(&#39;myEvent&#39;, this.handleEvent)
    }
    componentWillUnmount() {
        EventBus.removeEventListener(&#39;myEvent&#39;, this.handleEvent)
    }
    
    handleEvent = (e) => {
        console.log(e.detail.log)  //i&#39;m zach
    }
}
class ComponentB extends React.Component {
    sendEvent = () => {
        EventBus.dispatchEvent(&#39;myEvent&#39;, {log: "i&#39;m zach"}))
    }
    
    render() {
        return <button onClick={this.sendEvent}>Send</button>
    }
}

最后我们也可以不依赖浏览器提供的api,手动实现一个观察者模式,或者叫pub/sub,或者就叫EventBus。

function EventBus() {
  const subscriptions = {};
  this.subscribe = (eventType, callback) => {
    const id = Symbol(&#39;id&#39;);
    if (!subscriptions[eventType]) subscriptions[eventType] = {};
    subscriptions[eventType][id] = callback;
    return {
      unsubscribe: function unsubscribe() {
        delete subscriptions[eventType][id];
        if (Object.getOwnPropertySymbols(subscriptions[eventType]).length === 0) {
          delete subscriptions[eventType];
        }
      },
    };
  };

  this.publish = (eventType, arg) => {
    if (!subscriptions[eventType]) return;

    Object.getOwnPropertySymbols(subscriptions[eventType])
      .forEach(key => subscriptions[eventType][key](arg));
  };
}
export default EventBus;

10. Redux等

最后终于来到了大家喜闻乐见的Redux等状态管理库,当大家的项目比较大,前面讲的9种方法已经不能很好满足项目需求时,才考虑下使用redux这种状态管理库。这里就先不展开讲解redux了...否则我花这么大力气讲解前面9种方法的意义是什么???

总结

十种方法,每种方法都有对应的适合它的场景,大家在设计自己的组件前,一定要好好考虑清楚采用哪种方式来解决通信问题。

文初提到的那个小问题,最后我采用方案9,因为既然是小迭代项目,又是改别人的代码,当然最好避免对别人的代码进行太大幅度的改造。而pub/sub这种方式就挺小巧精致的,既不需要对别人的代码结构进行大改动,又可以满足产品需求。

更多编程相关知识,请访问:编程视频!!

以上がReact コンポーネント間で通信する 10 の方法の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事はsegmentfaultで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
Python vs. JavaScript:開発環境とツールPython vs. JavaScript:開発環境とツールApr 26, 2025 am 12:09 AM

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

JavaScriptはCで書かれていますか?証拠を調べるJavaScriptはCで書かれていますか?証拠を調べるApr 25, 2025 am 12:15 AM

はい、JavaScriptのエンジンコアはCで記述されています。1)C言語は、JavaScriptエンジンの開発に適した効率的なパフォーマンスと基礎となる制御を提供します。 2)V8エンジンを例にとると、そのコアはCで記述され、Cの効率とオブジェクト指向の特性を組み合わせて書かれています。3)JavaScriptエンジンの作業原理には、解析、コンパイル、実行が含まれ、C言語はこれらのプロセスで重要な役割を果たします。

JavaScriptの役割:WebをインタラクティブでダイナミックにするJavaScriptの役割:WebをインタラクティブでダイナミックにするApr 24, 2025 am 12:12 AM

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。

CおよびJavaScript:接続が説明しましたCおよびJavaScript:接続が説明しましたApr 23, 2025 am 12:07 AM

CおよびJavaScriptは、WebAssemblyを介して相互運用性を実現します。 1)CコードはWebAssemblyモジュールにコンパイルされ、JavaScript環境に導入され、コンピューティングパワーが強化されます。 2)ゲーム開発では、Cは物理エンジンとグラフィックスレンダリングを処理し、JavaScriptはゲームロジックとユーザーインターフェイスを担当します。

Webサイトからアプリまで:JavaScriptの多様なアプリケーションWebサイトからアプリまで:JavaScriptの多様なアプリケーションApr 22, 2025 am 12:02 AM

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

Python vs. JavaScript:ユースケースとアプリケーションと比較されますPython vs. JavaScript:ユースケースとアプリケーションと比較されますApr 21, 2025 am 12:01 AM

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。

JavaScript通訳者とコンパイラにおけるC/Cの役割JavaScript通訳者とコンパイラにおけるC/Cの役割Apr 20, 2025 am 12:01 AM

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

JavaScript in Action:実際の例とプロジェクトJavaScript in Action:実際の例とプロジェクトApr 19, 2025 am 12:13 AM

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!