基本的に怠Diary

主に日常と作ったものを書いていく。

Reactの`<Component />`と`Component()`の違いを理解する

ReactにおけるとComponent()の違い

をみて、疑問に感じたことに対するメモ

Reactでコンポーネントを呼び出す方法には2種類ある。<Component />Component()だ。見た目は似ているが、Reactの内部では全く異なる扱いを受ける。今回、この違いについてZennの記事を読んで学習した際に疑問がでてきたのでその部分をまとめる。

コンポーネント関数呼び出しの違い

コンポーネント呼び出し の場合:

  1. Appが再レンダリング → 新しいCounter関数が作られる
  2. Reactは前回と異なる関数 = 別のコンポーネントと認識
  3. 古いCounterをアンマウント → 新しいCounterをマウント
  4. SlowMountもアンマウント・マウントされるため、重い初回処理が毎回走る

関数呼び出し Counter()の場合:

  1. Appが再レンダリング → 新しいCounter関数が作られる
  2. しかしCounter()はただの関数実行なので、Reactは関与しない
  3. 返り値のJSX()が親に展開される
  4. SlowMountはファイルのトップレベルで定義されているため、Appの再レンダリングでも再生成されない
  5. Reactは「前回と同じSlowMountコンポーネント」と認識
  6. → 再レンダリングのみで、アンマウント・マウントは起きない

核心部分

  • <Component />コンポーネント呼び出し):Reactがライフサイクルを管理する
  • Component()(関数呼び出し):Reactはライフサイクルを管理しない

なぜライフサイクルの有無が生まれるのか

  • コンポーネント呼び出しはjsx()関数を経由してReact要素を生成
  • Reactはこのオブジェクトをもとにライフサイクルを管理
  • 関数呼び出しはReact要素を経由しないため、Reactの管理外

コンポーネント内でコンポーネントを定義する問題

  • 親の再レンダリングごとに新しい関数が作られる
  • Reactは「別のコンポーネント」と認識してアンマウント・マウント
  • 状態を持っている場合、リセットされる

render hooksパターン

const useChecks = (labels) => {
  const [checkList, setCheckList] = useState(...);
  const isAllChecked = checkList.every(x => x);
  
  const renderChecks = () => <Checks checkList={checkList} />;
  
  return [isAllChecked, renderChecks];
};

const App = () => {
  const [isAllChecked, renderChecks] = useChecks(labels);

  return (
    <>
      {renderChecks()}  {/* 関数呼び出し */}
      <button disabled={!isAllChecked}>次へ</button>
    </>
  );
};
  • カスタムフックからrenderComponent()のような関数を返す
  • 状態管理とUIをセットでカプセル化
  • 親には計算結果など必要な情報だけ公開
  • 使う場面は限定的だが、責務分離に有効

実務での使い分け

  • 基本は<Component />を使う
  • Component()はrender hooksパターンなど特殊なケースのみ

参考: - ReactにおけるとComponent()の違い - 【LINE証券 FrontEnd】コンポーネントをカスタムフックで提供してみた