メインコンテンツに移動
Reactive UIとは?state変化で自動更新するUI設計の考え方を整理

Reactive UIとは?state変化で自動更新するUI設計の考え方を整理

フロントエンド開発では、画面の一部を都度手動で書き換えるより、状態の変化に応じてUIが自然に更新される設計が標準的になっています。検索条件が変われば一覧が変わり、入力値が変わればエラーメッセージやボタン状態が変わり、ログイン状態が変われば表示されるメニューやページ全体の構成まで変わる、といった振る舞いは、いまや特別なものではありません。こうした背景の中で重要になるのが Reactive UI という考え方です。

Reactive UI は、state が変われば、その state を反映するように UI が更新される というモデルです。ただし、これは単なる便利機能ではありません。実際には、UI をどう設計するか、state をどこに置くか、イベントをどう扱うか、非同期処理とどう結びつけるか、コンポーネントをどの粒度で分けるかまで変える設計思想です。つまり、Reactive UI を理解するとは、「自動更新される画面」という表面だけでなく、なぜ state を中心にすると複雑な UI を整理しやすくなるのか を理解することでもあります。

また、Reactive UI はしばしば 「宣言的UI」 や 「リアクティブプログラミング」 と一緒に語られますが、完全に同じ意味ではありません。近い関係はありますが、何を中心に話しているのかを分けて整理したほうが理解しやすくなります。そこで本稿では、Reactive UI の定義から始めて、state と UI の関係、従来型の命令的UIとの違い、設計上のメリットと課題、実務での分割方針、さらに理解を助けるコード例までをまとめて掘り下げていきます。

1. Reactive UIとは

Reactive UI とは、UI を手動で直接書き換えるのではなく、state の変化を起点として UI を更新する考え方 です。言い換えると、画面は state の現在値を反映した結果として表現され、state が変われば、その state に対応する見た目へ UI が追随します。ここで重要なのは、「このボタンを押したらこの要素を表示する」といった命令を逐一書くことではなく、「この state なら UI はこう見えるべきだ」という対応関係を保つことです。つまり、Reactive UI の中心には、UI は state の写像である という発想があります。

この考え方が重要になるのは、現代の UI が静的なページではなく、常に状態変化の影響を受ける動的なシステムだからです。ローディング中か、入力途中か、エラー状態か、フィルタ条件は何か、データ取得は成功したか、権限はあるか、選択中アイテムは何か、といった複数の状態が同時に UI へ影響します。このような環境で、変更のたびに DOM や部品を個別命令で更新していくと、更新漏れや表示不整合が起こりやすくなります。Reactive UI は、この複雑さを 「state を変えれば、UI はその結果として変わる」 という構造へ整理することで、UI 制御を分かりやすくしようとします。

さらに言えば、Reactive UI は単なる描画の自動化ではなく、責務の分離 でもあります。イベントは state を変える、UI はその state を表現する、という役割の分け方が明確になることで、何が原因で画面が変わったのかを追いやすくなります。だから、Reactive UI を本当に理解するには、「便利だから使う」のではなく、「変化の多い UI を壊れにくく保つための考え方」として捉えることが大切です。

2. なぜReactive UIが重要になったのか

Reactive UI が重視されるようになった背景には、UI 自体の複雑化があります。かつてはページ遷移単位で画面が切り替わる設計が多く、表示の更新も比較的局所的でした。しかし現在のアプリケーションでは、一つの画面の中で検索、並び替え、入力途中の検証、ローディング、部分更新、フィルタ、モーダル、権限差分などが同時進行します。つまり、UI はもはや固定された表示物ではなく、複数の state が組み合わさって絶えず変形する構造 になっています。

こうした状況で、イベントごとに 「この要素を表示」「この要素を非表示」「このテキストを書き換え」 と命令していくと、変更ロジックが散らばりやすくなります。ある処理で一覧を更新し、別の処理でローディングを隠し、さらに別の処理でエラーメッセージを出す、といったコードが増えるほど、「この state では本来何が表示されているべきか」が見えにくくなります。Reactive UI は、この散らばりを 「state を一つの基準にして、表示はそこから導く」 という形で整理し直すために重要になりました。

2.1 画面が「固定物」ではなく「状態の結果」になった

いまの画面は、ページ読み込み時に一度描いて終わるものではありません。入力のたびに候補が変わり、通信結果で一覧が変わり、権限やルートで表示領域が変わります。つまり、画面は完成済みの成果物というより、state に応じて常に再構成される結果物です。この認識がないまま命令的に UI を積み上げると、各更新処理の関係が複雑になりやすくなります。

Reactive UI は、まさにこの点に対応しています。UI を状態の結果として扱うことで、「何が表示されるか」は state を見れば説明できるようになります。これにより、イベントハンドラの中を全部たどらなくても、表示ロジック全体の一貫性を保ちやすくなります。つまり、画面が state の結果になった現代では、Reactive UI は自然な設計モデルです。

2.2 非同期処理を扱いやすくなる

現代の UI は、ほとんどの場面で非同期通信と結びついています。データ取得中、成功、失敗、再試行、部分ローディング、ページ切り替えなど、非同期状態は UI へ直接影響します。命令的な更新では、通信の段階ごとに表示の切り替えを細かく管理しなければならず、処理が増えるほど整合性が崩れやすくなります。

Reactive UI では、たとえば loadingerrordata といった state を持たせ、その state に応じて UI が自然に切り替わるようにできます。すると、「いま何が見えるべきか」は非同期状態の値を見れば分かります。つまり、非同期の進行を命令列としてではなく、state 遷移として扱えることが、Reactive UI の大きな強みです。

2.3 チームでの理解共有がしやすい

複数人で UI を開発する場合、重要なのは「どのイベントでどの DOM を触るか」より、「この state なら UI はどうあるべきか」を共有できることです。命令的な実装では、イベントハンドラや副作用処理の中へ更新ロジックが散らばるため、レビュー時も保守時も全体の振る舞いを把握しにくくなります。

Reactive UI では、state と UI の対応関係が整理されやすいため、会話の単位も 「この state のときにこの表示でよいか」 になりやすいです。これは設計レビューやバグ切り分けで非常に役立ちます。つまり、Reactive UI の価値は個人の書きやすさだけではなく、チームの共通理解を作りやすくする点にもあります。

3. stateとUIの関係をどう捉えるべきか

Reactive UI の中心には、「UI は state の結果である」 という考え方があります。ボタンが押されたから色を変える、入力されたからエラーを出す、通信中だからローディングを表示する、といった現象を個別命令として追うのではなく、「押された状態」「入力値が不正な状態」「通信中状態」という state に応じて UI がそう見える、と捉えます。つまり、Reactive UI では、UI はイベント列の副作用ではなく、現在の状態を視覚化したもの として扱われます。

この視点を持つと、UI 更新の責任がかなり整理されます。イベントの責務は state を変えることに寄り、UI の責務はその state を正しく表現することに寄ります。すると、「どこで画面が変わったか」を追い回すより、「いまの state は何か」を見れば、画面のあり方を説明しやすくなります。つまり、Reactive UI の本質は自動更新そのものではなく、state を唯一の判断基準にしやすいこと にあります。

3.1 state は見た目を決める土台である

Reactive UI における state は、単なる変数の集まりではありません。画面が今どう見えるべきかを決める土台です。入力値、選択中のタブ、開いているモーダル、取得済みデータ、通信状態、権限、バリデーション結果など、UI の見た目へ影響するものは state として整理できます。つまり、state は UI の背後にある内部モデルであり、そのモデルが変われば表示も変わる、という対応関係を保つことが重要です。

このとき大切なのは、「state を持つこと」 自体が目的ではないという点です。state を増やせば整理されるわけではなく、どの UI 表示がどの state に依存しているかが分かるように持つ必要があります。たとえば、ローディング中かどうか、データがあるかどうか、選択行が何か、といった state は UI に直接効きますが、そこから計算できるものまで重複して保持すると、かえって混乱しやすくなります。つまり、state は多ければ良いのではなく、UI を決めるために必要な最小限かつ明確なものとして整理するべきです。

3.2 state 変更が UI 更新の原因になる

Reactive UI では、画面を変えることより先に state を変えることが中心になります。つまり、「ボタンを押したから表示する」のではなく、「isOpen を true にしたから表示される」 と考えるわけです。見た目の変化は原因ではなく結果になります。この転換は非常に重要で、どこで何を変更すべきかの責務分離を明確にしてくれます。

この考え方が強いのは、バグの切り分けでも有効だからです。画面が正しく切り替わらないとき、「state 自体がおかしいのか」「state は正しいのに描画条件が間違っているのか」を分けて見やすくなります。命令的な UI では、複数箇所の DOM 操作が絡み合っていると原因が追いにくいですが、Reactive UI では 「state 更新」 と 「描画ロジック」 が分かれるぶん、問題の場所を絞り込みやすくなります。つまり、state を起点にすると、更新理由の説明責任もはっきりします。

3.3 複数 state が絡むほど整理の重要性が増す

実務では、一つの state だけで画面が動くことはほとんどありません。入力値、検索条件、並び替え、権限、非同期ローディング、選択中状態、エラーなどが同時に UI を決めます。このとき、「どの state がどの表示へ効いているか」が曖昧だと、バグや意図しない再描画が増えます。

Reactive UI が有効なのは、こうした複数 state を 「今の画面はどの組み合わせの結果か」 として考えられるからです。ただし、その前提として、state 同士の役割分担が明確である必要があります。つまり、Reactive UI は勝手に整理してくれる仕組みではなく、整理しやすい枠組み を提供するものです。state 設計が曖昧なままだと、Reactive UI の利点も十分には出ません。

4. Reactive UIはどのように成立するのか

Reactive UI は、「state が変われば UI が変わる」 という短い説明で語られますが、その背後ではいくつかの段階が動いています。まず state を保持する層があり、その state を参照して UI を描画する仕組みがあります。そして、クリックや入力や通信結果などのイベントが state を更新し、その更新をきっかけに UI が再評価されます。つまり、Reactive UI は魔法の自動更新ではなく、state 保持・イベントによる更新・描画の再評価 が連動するモデルです。

この構造を理解しておくことは実務で重要です。なぜなら、Reactive UI を使っていても、どこで state が変わり、どの描画範囲が再評価されているのかを把握していなければ、不要な再描画や責務の混線が起きやすいからです。自動更新される結果だけ見ていると便利ですが、内部構造を知らないと最適化もデバッグも難しくなります。つまり、Reactive UI を本当に使いこなすには、その成立条件をある程度意識する必要があります。

4.1 state を保持する場所が必要になる

Reactive UI では、まず現在の状態を保存する場所が必要です。これはローカルなコンポーネント state かもしれませんし、複数画面で共有する global state かもしれません。いずれにしても、「いま何がどうなっているのか」 を保存する層がなければ、UI はそこから導けません。つまり、Reactive UI の出発点は state ストアの存在です。

ここで設計上大切なのは、その state をどこへ置くかです。入力欄の途中の値のような一時的なローカル情報と、ログイン状態やテーマ設定のような全体共有情報では、置くべき場所が違います。この区別が曖昧だと、state の責務が広がりすぎたり、逆に共有が必要な情報が閉じ込められたりします。Reactive UI では 「state を持つ」 こと以上に、「どこに持つか」 が重要です。

4.2 描画は state の現在値から再評価される

Reactive UI の描画ロジックは、「今の state なら UI はこうあるべきだ」 を表現する層です。たとえば loading が true ならローディング表示を出し、error があればエラーメッセージを出し、items があれば一覧を描く、といった形です。つまり、描画は個別命令の集まりではなく、state の現在値をもとに UI を組み立て直す作業です。

この再評価型の描画が重要なのは、一貫性を保ちやすいからです。表示のあちこちを手動で直すのではなく、state を一度正せば UI 全体が整合した形で更新されやすくなります。ただしそのぶん、state の粒度や依存範囲が広すぎると、必要以上に多くの部品が再評価されることがあります。つまり、Reactive UI は 「再評価される」 ことが前提だからこそ、描画境界の設計も重要になります。

4.3 イベントは state 更新の入口になる

クリック、入力、非同期通信の完了、タイマーといったイベントは、Reactive UI では UI を直接変える命令ではなく、主に state を更新する入口になります。ユーザーが何か操作したとき、そのイベントハンドラの責務は 「DOM をこう変える」 ことではなく、「state をこう変える」 ことへ寄ります。UI はその変化の結果として自然に切り替わります。

この発想があると、イベントハンドラの役割はかなり整理されます。たとえば 「モーダルを開く」 は、DOM にクラスを追加することではなく isModalOpen = true にすることだと捉えられます。その結果としてモーダルが表示されるなら、イベント処理は状態変更だけを考えればよくなります。つまり、Reactive UI はイベント処理を簡潔にしやすい構造でもあります。

4.4 簡単なコード例で見るReactive UIの骨格

以下のような小さなコードでも、Reactive UI の骨格は見えます。状態を持ち、その状態を表示へ反映し、ボタン操作で状態を変えるだけで、画面の見え方が変わります。

let state = { count: 0 };

function render() {
  document.getElementById("app").innerHTML = `
    <p>現在のカウント: ${state.count}</p>
    <button id="inc">増やす</button>
  `;

  document.getElementById("inc").addEventListener("click", () => {
    state = { ...state, count: state.count + 1 };
    render();
  });
}

render();

この例では手動で render() を呼んでいますが、考え方は十分に Reactive です。UI は state.count の現在値を表示しており、ボタン押下時に DOM を直接書き換えるのではなく、まず state を変え、その結果として UI を再描画しています。つまり、Reactive UI の本質はライブラリの有無ではなく、「UI が state の結果として定義されているかどうか」にあります。

5. 宣言的UIとの関係

Reactive UI はしばしば 宣言的UI と密接に結びつけて語られます。実際、両者は非常に相性が良く、現代的な UI フレームワークの多くはこの二つをセットで実現しています。ただし、完全に同じ概念として扱うと少し雑になります。Reactive UI は 「state が変化すると UI が追随する」 という更新モデルに焦点を当てているのに対し、宣言的UIは 「いまの state なら UI はこうあるべきだ」 と書く表現スタイルに焦点を当てているからです。つまり、Reactive UI は振る舞いのモデルであり、宣言的UIはそのモデルを記述しやすくする書き方だと考えると整理しやすくなります。

この違いを分けておくと、なぜ宣言的な書き方が Reactive UI と相性が良いのかが理解しやすくなります。state を入力として UI を決めるには、「この要素を表示しろ」「このテキストを書き換えろ」 と命令するより、「state がこうなら、この UI が出る」 と書くほうが自然だからです。つまり、宣言的UIは Reactive UI を見通しよく書くための表現形式です。

5.1 宣言的UIは「結果」を書く

宣言的UIでは、UI を手順ではなく結果として書きます。たとえば 「loading が true ならこのローディング画面を出し、それ以外なら一覧を出す」 のように、状態に応じた結果を書きます。これは DOM をどう操作するかの命令ではなく、いまの状態で画面がどう見えるべきかの定義です。

この書き方が強いのは、state と表示の関係がそのままコードへ現れるからです。読んだ瞬間に 「この state のときにこう見える」 と理解しやすくなります。つまり、宣言的UIは Reactive UI における state 中心の発想を、そのまま可読性の高い形へ落とし込みやすいです。

5.2 命令的UIとの違いがより明確になる

命令的UIでは、「ボタンを押したら A を表示し、B を隠し、エラーなら C を赤くする」 といった形で手順を細かく書きます。単純な画面ならそれでもよいですが、分岐や非同期状態が増えるほど命令列は散らばりやすくなります。Reactive UI は、この命令の散乱を state 中心へまとめやすくし、宣言的UIはその関係をコードとして表現しやすくします。

つまり、宣言的UIは単なる短い記法ではありません。「どう変えるか」 ではなく 「どうあるべきか」 を書くため、複雑な UI ほど差が大きくなります。これは Reactive UI のメリットを見た目レベルでも分かりやすくする要素です。

5.3 React のコード例で見る宣言的な Reactive UI

以下は非常に小さな例ですが、state に応じて UI を宣言的に切り替える Reactive UI の典型です。

import { useState } from "react";

function LoginPanel() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <div>
      <h2>{isLoggedIn ? "ようこそ" : "ログインしてください"}</h2>
      <button onClick={() => setIsLoggedIn(!isLoggedIn)}>
        {isLoggedIn ? "ログアウト" : "ログイン"}
      </button>
    </div>
  );
}

このコードでは、ボタンを押したときに DOM の見出しテキストやボタンラベルを直接操作していません。isLoggedIn という state が変わるだけで、それに応じて見出しとボタン表示が自動的に切り替わります。つまり、state が原因であり、UI はその結果として表現されています。これが宣言的な Reactive UI の典型例です。

6. 従来型UIとReactive UIの違い

Reactive UI を深く理解するには、従来型の命令的UIと比較するのが有効です。命令的UIでは、イベントが起きるたびにどの要素をどう更新するかを直接書きます。つまり、UI 変更は操作手順の集まりとして表現されます。一方、Reactive UI ではイベントは state を変えるだけであり、その state をもとに UI が再評価されます。つまり、表示は命令の結果というより、状態の結果として表れます。この差は小さく見えても、実際の保守性や複雑画面への耐性に大きく効いてきます。

6.1 命令的UIは更新手順が中心になる

命令的UIでは、「クリックされたらこの要素を表示する」「チェックが外れたらこの文言を戻す」「通信成功時に一覧を追加する」といった手順を個別に書きます。これは小さな画面では直感的で分かりやすいですが、状態や画面要素が増えるほど、「どこでどの要素を触っているのか」 が散らばりやすくなります。

その結果、表示不整合や更新漏れが起こりやすくなります。ある処理では表示したのに、別処理では非表示に戻していない、といった問題が起きやすいからです。つまり、命令的UIは単純なときは分かりやすくても、複雑さが増えるほど管理コストが上がります。

6.2 Reactive UIは状態の整合性を保ちやすい

Reactive UI では、「UI は state の現在値の表現である」 という前提があるため、個別要素の更新命令を散らす必要が減ります。状態が正しければ、表示もその state に対応する形でまとまりやすくなります。つまり、更新の整合性を state 側へ寄せられることが大きな利点です。

ただし、これは万能ではありません。state 設計が悪いと逆に分かりづらくなります。つまり、命令の複雑さを state 設計の質へ移している面もあります。それでも、複数要素が連動する現代的な画面では、Reactive UI のほうが全体の整合を保ちやすい場面が多いです。

6.3 比較コードで見る違い

命令的な書き方では、表示対象を直接操作しがちです。

const button = document.getElementById("toggle");
const panel = document.getElementById("panel");

button.addEventListener("click", () => {
  if (panel.style.display === "none") {
    panel.style.display = "block";
    button.textContent = "閉じる";
  } else {
    panel.style.display = "none";
    button.textContent = "開く";
  }
});

 

一方、Reactive な考え方では、まず状態を持ち、その状態から表示を決めます。

 

let state = { isOpen: false };

function render() {
  document.getElementById("app").innerHTML = `
    <button id="toggle">${state.isOpen ? "閉じる" : "開く"}</button>
    <div style="display:${state.isOpen ? "block" : "none"}">
      パネルの内容
    </div>
  `;

  document.getElementById("toggle").addEventListener("click", () => {
    state = { ...state, isOpen: !state.isOpen };
    render();
  });
}

render();

 

後者では、ボタン文言とパネルの表示は両方とも isOpen から導かれています。つまり、表示整合性の基準が一つになります。これが Reactive UI の基本的な強みです。

7. Reactive UIのメリット

Reactive UI が広く受け入れられているのは、複雑な UI を扱う際に実際のメリットが大きいからです。state と UI の対応が明確になりやすく、非同期通信との相性が良く、表示整合性を保ちやすく、コンポーネント単位で責務分離しやすい、といった点は実務上かなり効きます。つまり、Reactive UI は単にモダンなスタイルではなく、変化の多い現代的なアプリケーションを保守しやすくする方法として価値があります。

7.1 UI の整合性を保ちやすい

Reactive UI では、一つの state が複数要素の見た目を同時に決めることができます。そのため、「ボタンだけ更新されたがラベルは古い」「一覧は更新されたが件数表示が古い」 といった不整合が起こりにくくなります。もちろん state 設計が悪ければ問題は残りますが、少なくとも原理としては、一つの基準で複数表示を揃えやすいです。

これは、複数のUI要素が連動する画面で特に大きな利点になります。表示条件や権限条件が複雑になるほど、Reactive UI の整合性維持能力は価値を持ちます。

7.2 非同期通信と自然に結びつく

ローディング中、通信成功、通信失敗、再試行中といった非同期の進行は、state として表現しやすく、その state に応じて UI を切り替えるのが自然です。つまり、Reactive UI は通信の進行状況をそのまま画面へ反映しやすいです。

これにより、非同期の分岐が命令列として散らばるのを抑えやすくなります。エラー処理やローディング表示も、「その状態ならそう見える」 と書けるからです。

7.3 コンポーネント設計と相性が良い

Reactive UI は state と表示責務をコンポーネントごとにまとめやすいため、部品化との相性が良いです。ボタン、フォーム、一覧、カード、モーダルなどを、それぞれ必要な state を持つ単位として設計しやすくなります。これにより、再利用やテストもしやすくなります。

つまり、Reactive UI は自動更新モデルであると同時に、UI を小さな責務単位へ分ける設計とも相性が良いのです。

8. Reactive UIの課題と限界

Reactive UI は非常に有用ですが、導入すれば何でも自動的に整理されるわけではありません。むしろ、state の持ち方が曖昧なまま使うと、どこで何が変わるのか分かりにくくなったり、不要な再描画が増えたり、依存関係が複雑になったりすることがあります。つまり、Reactive UI は複雑さを消すのではなく、複雑さを state 設計へ集約する 側面があります。

8.1 state 設計が悪いと逆に分かりにくい

Reactive UI の品質は state 設計に大きく依存します。同じ意味の状態を複数箇所で持っていたり、本来派生でよい値を重複して保持したりすると、どれが真の状態なのか分かりにくくなります。つまり、Reactive UI は state を中心にするからこそ、state の曖昧さがそのまま UI の曖昧さになります。

したがって、Reactive UI では何を state として持ち、何を計算で導くべきかを意識して設計する必要があります。ここを誤ると、自動更新されても全体は分かりづらいままです。

8.2 不要な再描画が起きやすくなることがある

state が変われば UI が再評価されるという仕組みは便利ですが、state の依存範囲が広すぎたり、更新粒度が粗すぎたりすると、必要以上に広い範囲が再描画されることがあります。これはパフォーマンスの問題だけでなく、「なぜここまで更新されたのか」が見えにくくなる問題でもあります。

つまり、Reactive UI では更新の自動化と同時に、再評価範囲の設計も重要です。どこまでが一つの責務単位かを意識しないと、便利さが逆にノイズになります。

8.3 仕組みを理解しないとブラックボックス化する

Reactive UI フレームワークを使えば画面は動きますが、その動きをブラックボックスのまま使うと、「なぜ再描画されたのか」「なぜここは更新されないのか」 が分からなくなりやすいです。つまり、見た目が自動で変わるぶん、内部の因果を見失いやすい面があります。

そのため、少なくとも state 更新と描画再評価の関係は理解しておく必要があります。抽象化を利用するにしても、その抽象化が何をしているかを知らなければ、設計も改善も難しくなります。

9. 実務ではどう設計するとよいのか

Reactive UI を実務でうまく使うには、「自動更新されるから便利」 という感覚だけで進めるのではなく、どの state をどこに置き、どの UI がどの state に依存するのかを明確にする必要があります。つまり、Reactive UI の成否はライブラリ選択よりも、state と責務の切り分け方 に大きく左右されます。小さな画面では多少雑でも動きますが、画面規模が大きくなるほど、state の境界と更新範囲の設計が効いてきます。

また、state を増やしすぎないことも大切です。何でも state にすると、依存が増えて分かりづらくなります。逆に持つべき state を持たないと、UI の意味が曖昧になります。つまり、Reactive UI 設計の本質は、「何を state として明示するべきか」を見極めることです。

9.1 state は最小限かつ意味単位で持つ

よい設計では、state は UI を決めるために本当に必要なものだけを持ちます。入力値、選択状態、ローディング状態、取得結果など、表示へ直接影響するものは明示的に持つべきです。一方、そこから計算できる派生値まで重複して state として保持すると、整合性管理が難しくなります。

つまり、state は多いほど良いのではなく、「UI を説明するために必要な最小単位」 に絞るべきです。これにより、どの state がどの表示を決めているかが見えやすくなります。

9.2 ローカル state と共有 state を分ける

すべての state を全体共有にすると依存範囲が広がりやすく、逆にすべてをローカルへ閉じると整合性を取りにくくなります。入力フォームの途中状態のような局所情報と、ログイン状態やテーマ設定のような共有情報は分けて考える必要があります。

つまり、Reactive UI では state の種類だけでなく、そのスコープも設計対象です。どこで持つかを間違えると、更新が重くなったり、責務が曖昧になったりします。

9.3 コンポーネント境界を state 責務に合わせる

見た目だけでコンポーネントを分けると、state 更新責務との境界がずれることがあります。Reactive UI では、どの state に依存してどこまで更新されるのかを考えてコンポーネントを切ったほうが、保守性もパフォーマンスも安定しやすいです。

つまり、コンポーネントは見た目のまとまりだけでなく、state の依存のまとまりとしても分けるべきです。これにより、再描画範囲も責務範囲も見通しやすくなります。

9.4 React のコード例で見る実務的な形

以下は、検索入力と非同期データ取得を小さくまとめた例です。入力値、ローディング、結果、エラーを state として持ち、それに応じて UI を切り替えています。

 

import { useEffect, useState } from "react";

function SearchUsers() {
  const [query, setQuery] = useState("");
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    if (!query) {
      setUsers([]);
      setError("");
      return;
    }

    let cancelled = false;

    async function fetchUsers() {
      try {
        setLoading(true);
        setError("");

        const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`);
        if (!res.ok) throw new Error("取得に失敗しました");

        const data = await res.json();
        if (!cancelled) {
          setUsers(data);
        }
      } catch (e) {
        if (!cancelled) {
          setError("ユーザー一覧を取得できませんでした");
          setUsers([]);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchUsers();

    return () => {
      cancelled = true;
    };
  }, [query]);

  return (
    <section>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="ユーザー名で検索"
      />

      {loading && <p>読み込み中です...</p>}
      {error && <p>{error}</p>}

      {!loading && !error && users.length > 0 && (
        <ul>
          {users.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      )}

      {!loading && !error && query && users.length === 0 && (
        <p>該当するユーザーはいません</p>
      )}
    </section>
  );
}

 

この例で重要なのは、UI を直接書き換えていないことです。入力変更は query を変え、通信結果は usersloadingerror を変え、その結果として UI が切り替わります。つまり、イベントや非同期処理は state を更新するだけで、表示は state の結果として定義されています。これが実務的な Reactive UI の典型です。

おわりに

Reactive UI とは、state が変化したときに、それに応じて UI が更新されるモデルです。ユーザーが示した定義は非常に本質的で、そのまま核心を表しています。ただし、実際にはそれは単なる 「自動更新」 の話ではなく、UI を state の結果として捉え、イベントは state を変える入口とし、表示整合性を state 中心で保つための設計思想でもあります。

重要なのは、Reactive UI を便利な仕組みとしてだけでなく、「複雑な画面を整理して扱う方法」 として理解することです。そうすれば、なぜ宣言的UIと相性が良いのか、なぜ非同期処理で強いのか、なぜ state 設計が重要なのかが自然につながって見えてきます。同時に、state が乱雑ならかえって分かりにくくなること、不必要な再描画が問題になることも理解しやすくなります。

Reactive UI を本当に使いこなすには、フレームワークの構文だけではなく、「どの state がどの UI を決めているのか」 を説明できるようになることが大切です。そこまで整理できるようになると、Reactive UI は単なる流行の開発スタイルではなく、変化の多い現代的なアプリケーションを壊れにくく、説明しやすく、拡張しやすくするための土台として見えるようになります。

LINE Chat