コンポーネント間での状態の共有
2つのコンポーネントの状態を常に連動させて変更したい場合があります。そのためには、両方のコンポーネントから状態を取り除き、それらを最も近い共通の親コンポーネントに移動し、propsを通じて子コンポーネントに渡します。これは状態のリフトアップと呼ばれ、Reactコードを書く際に最も一般的に行うことの一つです。
学習内容
- 状態をリフトアップすることでコンポーネント間で状態を共有する方法
- 制御コンポーネントと非制御コンポーネントとは何か
例による状態のリフトアップ
この例では、親のAccordionコンポーネントが2つの独立したPanelをレンダリングします:
AccordionPanelPanel
各Panelコンポーネントは、その内容が表示されるかどうかを決定するブール値のisActive状態を持っています。
両方のパネルの「表示」ボタンを押してください:
一方のパネルのボタンを押してももう一方のパネルには影響しないことに注意してください。これらは独立しています。


最初は、各PanelのisActive状態はfalseなので、両方とも折りたたまれて表示されます。


どちらかのPanelのボタンをクリックすると、そのPanelのisActive状態だけが更新されます。
しかし、ここで、常に1つのパネルだけが展開されるように変更したいとします。その設計では、2番目のパネルを展開すると最初のパネルが折りたたまれるはずです。どのようにすればよいでしょうか?
これら2つのパネルを調整するには、3つのステップでそれらの状態を親コンポーネントに「リフトアップ」する必要があります:
- 子コンポーネントから状態を削除します。
- 共通の親からハードコードされたデータを渡します。
- 共通の親に状態を追加し、イベントハンドラと一緒に渡します。
これにより、Accordionコンポーネントが両方のPanelを調整し、一度に1つだけ展開できるようになります。
ステップ1: 子コンポーネントから状態を削除する
あなたは、PanelのisActiveの制御をその親コンポーネントに与えます。これは、親コンポーネントがisActiveをpropとしてPanelに渡すことを意味します。まず、この行をコンポーネントからPanel削除します:
その代わりに、isActiveをPanelのpropsのリストに追加します:
これで、Panelの親コンポーネントは、制御isActiveをpropsとして渡すことで行えます。逆に、Panelコンポーネントは、制御権を持たなくなりました。isActiveの値は親コンポーネント次第です!
ステップ2:共通の親からハードコードされたデータを渡す
状態をリフトアップするには、調整したい両方の子コンポーネントの最も近い共通の親コンポーネントを見つける必要があります:
Accordion(最も近い共通の親)PanelPanel
この例では、Accordionコンポーネントです。両方のパネルの上位にあり、それらのpropsを制御できるため、現在どのパネルがアクティブであるかの「信頼できる情報源」になります。Accordionコンポーネントに、ハードコードされたisActiveの値(例えば、true)を両方のパネルに渡すようにします:
ハードコードされたisActiveの値をAccordionコンポーネント内で編集して、画面の結果を確認してみてください。
ステップ3:共通の親に状態を追加する
状態をリフトアップすると、状態として保存する内容の性質が変わることもよくあります。
この場合、一度にアクティブになるパネルは1つだけです。つまり、共通の親コンポーネントであるAccordionは、どのパネルがアクティブであるかを追跡する必要があります。boolean値の代わりに、アクティブなPanelのインデックスを表す数値を状態変数として使用できます:
activeIndexが0のときは最初のパネルがアクティブで、1のときは2番目のパネルがアクティブです。
どちらかのPanel内の「Show」ボタンをクリックすると、Accordion内のアクティブインデックスを変更する必要があります。Panelは、activeIndex状態を直接設定できません。なぜなら、それはAccordion内で定義されているからです。Accordionコンポーネントは、明示的に許可する必要があります。Panelコンポーネントがその状態を変更できるように、イベントハンドラをpropsとして渡すことで:
<button>内部のPanelは、クリックイベントハンドラとしてonShowpropを使用するようになります:
これで状態のリフトアップが完了しました!状態を共通の親コンポーネントに移動することで、2つのパネルを連携させることができました。「表示中」フラグを2つ使う代わりにアクティブなインデックスを使用することで、一度にアクティブになるパネルは1つだけであることを保証しました。また、イベントハンドラを子コンポーネントに渡すことで、子が親の状態を変更できるようにしました。


最初、AccordionのactiveIndexは0なので、最初のPanelはisActive = trueを受け取ります


当AccordionのactiveIndex状態が1に変化すると、代わりに2番目のPanelがisActive = trueを受け取ります
各状態に対する単一の信頼できる情報源
Reactアプリケーションでは、多くのコンポーネントが独自の状態を持ちます。入力のような、ツリーの下部にあるリーフコンポーネントに「近い」状態もあります。他の状態は、アプリの上部に「近い」場合もあります。例えば、クライアントサイドのルーティングライブラリでさえ、通常は現在のルートをReactの状態に保存し、propsで渡すことで実装されています!
一意の状態ごとに、それを「所有する」コンポーネントを選択します。この原則は、「単一の信頼できる情報源」を持つこととしても知られています。すべての状態が1か所にあるという意味ではありません。状態の各断片に対して、その情報を保持する特定のコンポーネントがあるということです。コンポーネント間で共有状態を複製するのではなく、それを共通の親にリフトアップし、それを必要とする子に渡します。
アプリは作業中に変化します。各状態の断片がどこに「存在する」かをまだ把握している間、状態を下に移動したり、戻したりすることは一般的です。これはプロセスの一部です!
さらに多くのコンポーネントで実際にどのように感じるかを確認するには、Reactでの考え方をお読みください。
まとめ
- 2つのコンポーネントを連携させたい場合、それらの状態を共通の親コンポーネントに移動します。
- 次に、共通の親コンポーネントからpropsを通じて情報を渡します。
- 最後に、イベントハンドラを渡して、子コンポーネントが親の状態を変更できるようにします。
- コンポーネントを「制御された」(propsによって駆動される)または「制御されていない」(状態によって駆動される)ものとして考えると便利です。
いくつかの課題を試してみましょう
Challenge 1 of 2:同期された入力 #
これら2つの入力は独立しています。それらを同期させてください: 一方の入力を編集すると、もう一方の入力も同じテキストで更新されるようにし、その逆も同様にします。
