コンポーネントを通信させる方法は複数あります。ユースケースに適したものもあります。ここに、私が知っていると役立つと思ったもののリストを示します。
反応する
親子ダイレクトコミュニケーション
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
ここで、子コンポーネントは親によって提供されたコールバックを値とともに呼び出し、親は親の子によって提供された値を取得できます。
アプリの機能/ページを構築する場合は、コールバック/状態 (container
またはとも呼ばれますsmart component
) を管理する単一の親を持ち、すべての子をステートレスにして、親にのみ報告することをお勧めします。このようにして、親の状態をそれを必要とする子に簡単に「共有」できます。
環境
React Context を使用すると、コンポーネント階層のルートで状態を保持し、この状態を非常に深くネストされたコンポーネントに簡単に挿入できます。小道具をすべての中間コンポーネントに渡す必要はありません。
これまで context は実験的な機能でしたが、React 16.3 で新しい API が利用可能になりました。
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
コンシューマーはrender prop / children 関数パターンを使用しています
詳細については、このブログ投稿を確認してください。
React 16.3 より前は、非常によく似た API を提供する react-broadcastを使用し、以前のコンテキスト API を使用することをお勧めします。
ポータル
通常の親子関係のように、2 つのコンポーネントを近づけて単純な関数と通信させたい場合にポータルを使用します。それが意味する視覚的/CSSの制約(z-index、不透明度など)。
この場合、「ポータル」を使用できます。ポータルを使用するさまざまな反応ライブラリがあり、通常はモーダル、ポップアップ、ツールチップに使用されます...
次の点を考慮してください。
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
内部でレンダリングすると、次の DOM を生成できますreactAppContainer
。
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
詳細はこちら
スロット
どこかにスロットを定義してから、Render Tree の別の場所からスロットを埋めます。
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
これはポータルと少し似ていますが、塗りつぶされたコンテンツが定義したスロットにレンダリングされることを除けば、ポータルは通常、新しい dom ノード (多くの場合、document.body の子ノード) をレンダリングします。
react-slot-fillライブラリを確認する
イベントバス
Reactのドキュメントに記載されているように:
親子関係のない 2 つのコンポーネント間の通信のために、独自のグローバル イベント システムを設定できます。componentDidMount() でイベントをサブスクライブし、componentWillUnmount() でサブスクライブを解除し、イベントを受け取ったら setState() を呼び出します。
イベントバスのセットアップに使用できるものはたくさんあります。リスナーの配列を作成するだけで、イベントの発行時にすべてのリスナーがイベントを受け取ります。または、 EventEmitterやPostalJsなどを使用できます
フラックス
Fluxは基本的にイベント バスですが、イベント レシーバーはストアです。これは、状態が React の外部で管理されることを除いて、基本的なイベント バス システムに似ています。
元の Flux 実装は、ハックな方法でイベント ソーシングを行う試みのように見えます。
Reduxは私にとって、イベント ソーシングに最も近い Flux 実装であり、タイム トラベル機能などのイベント ソーシングの利点の多くを利用します。React に厳密にリンクされているわけではなく、他の機能ビュー ライブラリでも使用できます。
Egghead の Reduxビデオ チュートリアルは非常に素晴らしく、内部でどのように機能するかを説明しています (非常にシンプルです)。
カーソル
カーソルはClojureScript/Omから来ており、React プロジェクトで広く使用されています。それらは、React の外部で状態を管理することを許可し、コンポーネント ツリーについて何も知る必要なく、状態の同じ部分への読み取り/書き込みアクセスを複数のコンポーネントに許可します。
ImmutableJS、React-cursors、Omniscientなど、多くの実装が存在します
Edit 2016 : 人々はカーソルが小さなアプリではうまく機能することに同意しているようですが、複雑なアプリではうまくスケーリングしません. Om Next にはもうカーソルがありません (最初にコンセプトを導入したのは Om ですが)。
Elm アーキテクチャ
Elm アーキテクチャは、 Elm 言語で使用するために提案されたアーキテクチャです。Elm が ReactJS でなくても、Elm アーキテクチャは React でも実行できます。
Redux の作成者である Dan Abramov は、React を使用して Elm アーキテクチャを実装しました。
Redux と Elm はどちらも非常に優れており、フロントエンドでイベント ソーシングの概念を強化する傾向があり、どちらもタイム トラベルのデバッグ、元に戻す/やり直し、再生を可能にします...
Redux と Elm の主な違いは、Elm は状態管理に関してより厳密になる傾向があることです。Elm では、ローカル コンポーネントの状態やマウント/アンマウント フックを使用することはできず、すべての DOM の変更はグローバルな状態の変更によってトリガーされる必要があります。Elm アーキテクチャは、単一の不変オブジェクト内のすべての状態を処理できるスケーラブルなアプローチを提案しますが、 Reduxは、単一の不変オブジェクトでほとんどの状態を処理するように勧めるアプローチを提案します。
Elm の概念モデルは非常に洗練されており、アーキテクチャは大規模なアプリにうまくスケーリングできますが、実際には、マウント後に入力にフォーカスを当てたり、既存のライブラリと統合したりするなどの単純なタスクを達成するのは困難であるか、より多くのボイラープレートを必要とする場合があります。命令型インターフェース (つまり、JQuery プラグイン) を使用します。関連する問題。
また、Elm アーキテクチャには、より多くのコードボイラープレートが含まれます。それほど冗長でも複雑でもありませんが、Elm アーキテクチャは静的に型付けされた言語により適していると思います。
FRP
RxJS、BaconJS、Kefir などのライブラリを使用して FRP ストリームを生成し、コンポーネント間の通信を処理できます。
たとえば、Rx-Reactを試すことができます
これらのライブラリを使用することは、ELM 言語がシグナルで提供するものを使用することに非常に似ていると思います。
CycleJSフレームワークは ReactJS を使用せず、 vdom を使用します。Elm アーキテクチャと多くの類似点があります (ただし、vdom フックが許可されているため、実際にはより使いやすくなっています)。関数の代わりに RxJ を広く使用しているため、FRP を使用したい場合に良いインスピレーションの源になります。反応する。CycleJs Egghead の動画は、その仕組みを理解するのに役立ちます。
CSP
CSP (Communicating Sequential Processes) は現在人気があります (主に Go/goroutines と core.async/ClojureScript のため) が、JS-CSPを使用して JavaScript でも使用できます。
James Long は、React での使用方法を説明するビデオを作成しました。
サガ
サガは、DDD / EventSourcing / CQRS の世界に由来するバックエンドの概念であり、「プロセス マネージャー」とも呼ばれます。これはredux-sagaプロジェクトによって一般化されており、主に副作用 (API 呼び出しなど) を処理するための redux-thunk の代替として使用されています。ほとんどの人は現在、それが副作用のためだけに役立つと考えていますが、実際にはコンポーネントの分離に関するものです.
サガは最後に Flux アクションを発行するため、まったく新しい通信システムというよりは、Flux アーキテクチャ (または Redux) を補完するものです。アイデアは、widget1 と widget2 があり、それらを分離したい場合、widget1 から widget2 をターゲットとするアクションを起動できないということです。したがって、widget1 は自分自身を対象とするアクションのみを起動し、saga は widget1 のアクションをリッスンする「バックグラウンド プロセス」であり、widget2 を対象とするアクションをディスパッチする場合があります。サガは 2 つのウィジェット間の結合点ですが、ウィジェットは分離されたままです。
興味がある場合は、こちらの私の回答をご覧ください
結論
これらの異なるスタイルを使用した同じ小さなアプリの例を見たい場合は、このリポジトリのブランチを確認してください。
長期的にはどのオプションが最適かはわかりませんが、Flux がイベント ソーシングのように見える点がとても気に入っています。
イベント ソーシングの概念がわからない場合は、この非常に教育的なブログをご覧ください: Turning the database inside out with apache Samza 。Fluxが優れている理由を理解するために必読です (ただし、これは FRP にも当てはまる可能性があります)。 )
コミュニティは、最も有望な Flux 実装がReduxであることに同意していると思います。Reduxは、ホット リロードのおかげで非常に生産的な開発者エクスペリエンスを徐々に可能にします。Bret Victor のInventing on Principle ビデオの印象的なライブコーディングが可能です!