あまり知られていないReactコンポーネントのchildrenの話
はじめに
今日は「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
コンポーネントのprops
にchildren
を追加し、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
に置き換えられる -
children
はReact.createElement
で生成されない - 公式ドキュメントおもしろい
Discussion
1のパータンだとparentが実行されましたら、ChildのReact.Elementが再つくりになります
2のパータンだと、ChildのReact.Elementが再つくりかどうかはParentの実行と関係なくなります。