👶

あまり知られていないReactコンポーネントのchildrenの話

2022/11/15に公開約3,400字1件のコメント

はじめに

今日は「React完全に理解した」人ならもしかしたらまだ知らないchildrenのお話になります。「Reactちょっとだけわかる」人からしたら当たり前のような内容になっておりますので、その点ご了承ください。

まずは基本のキ

以下のようなParentコンポーネントとChildコンポーネント、そしてAppコンポーネントがあったとします。

import { useState } from "react";

const Child = () => {
  console.log(`Child is render.`);
  return <p>Click me!!</p>;
};

const Parent = () => {
  const [count, setCount] = useState(0);

  return (
    <div onClick={() => setCount(count + 1)}>
      <Child />
    </div>
  );
};

export const App = () => {
  return <Parent />;
};

Parentコンポーネントは内部で状態を持っており、テキストをクリックすることで状態が更新され再レンダリングが走ります。その際、内部で呼んでいるChildコンポーネントも再レンダリングが走ります。

動作はこちらからご確認ください。テキストをクリックするたびにChildコンポーネントのconsole.logが実行されていることが確認できると思います。

続いて基本のホ

次にParentコンポーネントとAppコンポーネントを以下のように改変します。変更点としては、Parentコンポーネントのpropschildrenを追加し、ChildコンポーネントはParentコンポーネントのchildrenとして渡すようにします。Childコンポーネントは何も変更していません。

- const Parent = () => {
+ const Parent = (props) => {
    const [count, setCount] = useState(0);

    return (
      <div onClick={() => setCount(count + 1)}>
-       <Child />
+       {props.children}
      </div>
    );
  };

  export const App = () => {
-   return <Parent />;
+   return (
+     <Parent>
+       <Child />
+     </Parent>
+   );
  };

さきほどと同じようにテキストをクリックしてみると、前回と違ってChildコンポーネントが再レンダリングされていないことがわかります。動作はこちらからご確認ください。

最後に基本のン

ここからが今回お伝えしたかった内容になります。
なぜpropsで受け取ったChildコンポーネントは再レンダリングされなかったのでしょう?
その答えはJSXをReact.createElementに置き換えることでわかりやすくなります。

Parentの中にChild持ってるパターン

まずはParentコンポーネントの中からChildコンポーネントを呼ぶケースでJSXをReact.createElementを使って置き換えてみます。

// before
<div onClick={() => setCount(count + 1)}>
  <Child />
</div>

// after
React.createElement(
  "div",
  { onClick: () => setCount(count + 1) },
  React.createElement(Child, null, null) // ここでChildを生成している
)

見てわかる通り、Childコンポーネントを生成するReact.createElementがあります。これはParentコンポーネントがレンダリング時にChildコンポーネントを生成していることを意味しています。

React.createElementに書き換えたコードの動作はこちらからご確認ください。

Parentの外からChildもらうパターン

続いてChildコンポーネントをchildrenでもらっている場合も、JSXをReact.createElementで置き換えてみましょう。

// before
<div onClick={() => setCount(count + 1)}>
  {props.children}
</div>

// after
React.createElement(
  "div",
  { onClick: () => setCount(count + 1) },
  props.children // ここでChildを生成していない
)

こちらは先ほどと違い、childrenを新たに生成するためのReact.createElementが見当たりません。つまり、Parentコンポーネントのレンダリング時もpropsで受け取ったchildrenをそのまま使っているだけです。React.createElementを使って生成していないのでChildコンポーネントは再レンダリングされなかったという訳です。

こちらのコードの動作はこちらからご確認ください。

さいごに

JSXをReact.createElementに置き換えられる話は公式にもあります。なかなか全てのドキュメントに目を通すのは難しいですが、たまにパラパラみていると意外な情報があって面白いです。

まとめ

  • JSXはReact.createElementに置き換えられる
  • childrenReact.createElementで生成されない
  • 公式ドキュメントおもしろい

Discussion

1のパータンだとparentが実行されましたら、ChildのReact.Elementが再つくりになります
2のパータンだと、ChildのReact.Elementが再つくりかどうかはParentの実行と関係なくなります。

ログインするとコメントできます