Open9

React公式ページからの学び

YujiYuji

Stateにするかどうかの判断方法

以下の観点を自問自答する!!!

  • 時間が経っても変わらないものですか? そうであれば、state ではありません。
  • 親から props 経由で渡されるものですか? そうであれば、state ではありません。
  • コンポーネント内にある既存の state や props に基づいて計算可能なデータですか? そうであれば、それは絶対に state ではありません!
YujiYuji

ボトムアップ?トップダウン?

小規模であればトップダウン。大規模であればボトムダウン

YujiYuji

意識したいこと

  • React コンポーネントとは、マークアップを添えることができる JavaScript 関数

  • 再レンダー間で state を維持したい場合、ツリーの構造はレンダー間で「合致」する必要がある

  • Context apiの使う際の注意(以下のようにすると、propsの受け渡しを減らせる)

    • コンポーネントを抽出して、children を JSX として渡す方法を検討しましょう。もし、何らかのデータを、それを必要とせずただ下に流すだけの中間コンポーネントを何層も経由して受け渡ししているような場合、何かコンポーネントを抽出するのを忘れているということかもしれません。たとえば、<Layout posts={posts} /> のような形で、データを直接使わないビジュアルコンポーネントに post のようなデータを渡しているのかもしれません。代わりに、Layout は children を props として受け取るようにし、<Layout><Posts posts={posts} /></Layout> のようにレンダーしてみましょう。これにより、データを指定するコンポーネントとそれを必要とするコンポーネントの間のレイヤ数が減ります。
YujiYuji

Propsについて

コンポーネントは時間経過とともに別の props を受け取る可能性があるということを示しています。props は常に固定だとは限らないのです! ここでは time プロパティは毎秒変化していますし、color プロパティもあなたが別の色を選択するたびに変化します。props とはコンポーネントの最初の時点ではなく、任意の時点でのコンポーネントのデータを反映するものなのです。

  • props とはある時点での読み取り専用のスナップショットである。レンダー毎に新しいバージョンの props を受け取る。
  • Propsは、常に不変である。
    • 「props の書き換え」はしてはいけない。書き換えたい場合は、親からstateの書き換え用のstateのセットが必要。
  • Propsの値は、変更しうる可能性がある。
YujiYuji

条件付きレンダー

AND演算子 &&

AND演算子は、左側がtrueの場合、右側の値を返す、そうでない場合は、falseを返す。
Reactでは、false を JSX ツリーの「穴」と見なし、null や undefined と同様に、何もレンダー

export const Practice = () => {
  const isOk = false;
  return <div>{isOk && `Ok`}</div>;
};

注意
左側が0の場合は、trueとして判断される。なので、0をfalseと扱いたい場合は、以下のように書く。

messageCount > 0 && <p>Hello</p>

YujiYuji

イベントハンドラ

イベントハンドラに関数を渡す際に、handleAlert()のように渡してしまうと呼び出した際に発火させてしまう。

export const Practice = () => {
  const handleAlert = () => {
    alert("Hello");
  };
  return (
    <div>
      <button onClick={handleAlert()}>alert</button>
    </div>
  );
};

本来望んでいる挙動としては、レンダリングされた際に発火ではなく、クリックなどの動作のイベントの際に発火させたい。
その際は、以下のように関数名だけを記載し渡す。

export const Practice = () => {
  const handleAlert = () => {
    alert("Hello");
  };
  return (
    <div>
      <button onClick={handleAlert}>alert</button>
    </div>
  );
};
YujiYuji
  • ローカル変数はレンダー間で保持されません。React がこのコンポーネントを次にレンダーするときは、まっさらな状態からレンダーします。過去にローカル変数を変更したことは考慮されません。
  • ローカル変数の変更は、レンダーをトリガしません。新しいデータでコンポーネントを再度レンダーする必要があることに React は気づきません。
YujiYuji

レンダーとコミット

レンダーのトリガーは2つ

  • コンポーネントの初回のレンダリング時
  • コンポーネント(またはその祖先のいずれか)stateの値が変更された時

レンダーとは?

Reactがコンポーネントを呼び出すこと。
初回のレンダー時は、rootコンポーネントを呼び出す。
次回からは、stateに更新によって、レンダーがトリガーされたコンポーネントを呼び出す。

Reactは、ネストされたコンポーネントがなくなるまで、レンダーし続ける。
つまり、親コンポーネントでstateが更新された場合は、子や孫などのコンポーネントまでレンダリングする仕組みになっている。
上記はおそらく、useMemoなどで、パフォーマンス改善が必要になってきそう。

補足
Strict Modeを設定している場合は、開発環境のみで、コンポーネントを2回レンダリングする仕組みになっている。
理由としては、コンポーネントは常に純粋である必要があり、値の状態が変わらない限り何度もレンダリングされても同じ結果が帰ることを2回レンダリングさせることで保証するため。

DOMの変更コミット

  • 初回レンダー時には、React は appendChild() DOM API を使用して、作成したすべての DOM ノードを画面に表示します。
  • 再レンダー時には、React は最新のレンダー出力に合わせて DOM を変更するため、必要な最小限の操作(レンダー中に計算されたもの!)を適用します。

レンダー時に違いがある場合のみ、DOMノードを変更する仕組みになっている。

レンダー結果が前回と同一である場合、React は DOM を触らない。

DOMへの変更コミットが完了すると、ブラウザの再描画が発生する。