本記でまとめる状態の知識分野
・状態管理とatomicdesign
・メモ化とパフォーマンス
・redux
指針
状態設計を意識したフロー
必要な状態を洗い出す:
UI にどのような状態が必要かを明確化。
状態を分類(ローカル、グローバル、サーバー)。
状態の配置を決定する:
状態を必要とするコンポーネントの位置を特定。
配置のルールに基づき、適切な場所で管理。
*状態を「最も近い共通祖先」に配置
- 状態を下位層に押し込めすぎない
リフローを最小化:
状態を分割し、適切な構造で管理。
メモ化を活用。
将来の拡張性を考慮:
状態管理ライブラリの導入を検討。
グローバルとローカルのバランスを最適化。
状態管理とatomicdesign
-
Atoms (原子)
・役割:UI の最小単位として、個別の機能を提供。責務を持たず、単純な表示やスタイルに集中。
・ロジック:ほぼロジックを持たない。必要であれば、クリックや入力イベントのような基本的なインタラクションを扱う程度。ロジックの実装は親コンポーネントに委譲。
・状態:状態は持たず、必要なデータをすべて props として受け取る。 -
Molecules (分子)
・役割:複数の Atom を組み合わせて、単一の機能を持つ UI 部品を構成。Atom 間の簡単な連携や、単一のユースケースに対応。
・ロジック:子コンポーネント(Atoms)のインタラクションの連携。軽度のビジネスロジック(例: 入力値の検証)。
・状態:状態を持たないのが原則。ただし、必要に応じて、ローカルな一時状態を持つ場合もある(例: フォーカスの管理)。状態管理の責務は、Organisms または Pages に委譲。 -
Organisms (有機体)
・役割:Molecules と Atoms を組み合わせて、機能的なセクションを形成。例: ナビゲーションバー、検索フォーム、ダッシュボードのウィジェットなど。
・ロジック:UI セクション内のロジックを管理。子コンポーネント間のデータフロー。ユーザーアクションの処理(例: 検索ボタンが押されたら API リクエストを親に通知)。一部のビジネスロジックを持つことが許されるが、複雑なロジックは上位層に委譲。
・状態:必要な場合、セクション内で使用するローカル状態を管理。UI の制御(例: モーダルの開閉状態)。操作中のデータの一時保存(例: フォームの入力内容)。グローバルな状態(例: アプリ全体のテーマ)は Context API や Redux を通じて受け取る。 -
Templates (テンプレート)
・役割:ページのレイアウトや構造を定義。Organisms を配置して、全体的な UI の枠組みを提供。例: ヘッダー、サイドバー、メインコンテンツの配置。
・ロジック:ロジックは基本的に持たない。Organisms 間のデータの受け渡しや、テンプレート自体の構造に関する軽微なロジックのみ。
・状態:状態を持たない。必要なデータや状態は親コンポーネント(Pages など)から受け取り、Organisms に渡す。 -
Pages (ページ)
・役割:完全な画面を構築するためのエントリーポイント。Templates にデータやロジックを注入し、最終的な画面を形成。アプリケーションの中で一番上位に位置し、API コールやグローバル状態管理を行う。
・ロジック:ビジネスロジックや非同期処理を実行。データ取得(例: API コール)。状態の変化に応じてテンプレートや Organisms を動的に切り替える。アプリ全体に関わるロジックをここで処理する。
・状態:グローバルな状態やページ単位の状態を管理。ページ内のデータ(例: 検索クエリやフィルタ設定)。ページの初期化状態(例: ローディング状態、エラーハンドリング)。必要に応じて、状態管理ツール(例: Redux、Recoil)を利用。
メモ化とパフォーマンス
再レンダリングの条件
①propsの変更
②ローカルstateの変更
③そのコンポーネントが使用しているグローバルstateの変更
③親コンポーネントの再レンダリング
これをもとにDOMツリーが更新される
メモ化、その他不要なレンダリングを抑える手法
①React.memo
・基本的な仕組み:
親コンポーネント再レンダリングの際に渡すプロップスに変更がなければ子コンポーネントの再レンダリングをスキップする
・注意点:
react.memoは浅い比較を行う。親コンポーネント内で定義された配列やオブジェクトは毎回別のインスタンスとして扱われる。
つまり毎回参照の異なる配列やオブジェクトが生成されることで内容が同じでも別のものとしてpropsが渡されてしまうためusememoなどのhooksと組み合わせて使う必要がある。
②useMemo
・基本的な仕組み:
依存関係に変更がない場合再計算を行わずに以前の値を利用する。
再計算が行われると再レンダリングが起こる。
・使用例:
react.memoと組み合わせた不要な再レンダリングの防止。再レンダリング時のオブジェクトの再生成を防ぐ
// オブジェクトをuseMemoでメモ化
const config = useMemo(() => {
return { theme: 'dark' };
}, []);
・特定の値の変更に依存して行われる関数の再実行(副作用の伴わない場合)
*副作用:props,stateに基づいてUIを制御する処理以外の処理。API通信。タイマー管理など。タイミングの制御が難しいためuseEffectによって特別にレンダリング後に実行を行うように制御しなければならない。
③useCallback
・基本的な仕組み:
関数の再生成を防ぐ
・使用例:
関数をpropsとして渡すときにusememoと組み合わせて使用。
親コンポーネントが再レンダリングされても依存配列に変更がなければプロップスに変更は起こらないため不要な再レンダリングを防止できる。
④useRef
・基本的な仕組み:
値の保持。useMemoとの違いは依存配列を必要としないこと、値が更新されても再レンダリングが起こらないこと
・使用例:
ソケットインスタンスの再生成による不要な再接続を防止する
redux
別記事にまとめる