◆ <Child /> だけだと作った段階ではコンポーネントのレンダリング結果は含まれない
◆ レンダリング処理が行われると結果がオブジェクトに追加される
◆ createElement の結果をメモしておけばレンダリング処理を再実行せずに済む

React のドキュメントの FAQ を見ていると

便利なことに、useMemo は子コンポーネントの計算量の高い再レンダーをスキップするのにも使えます:

というのを見かけました
コードはこうなっています

function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}

これを見た時 これって意味あるの?と思いました
useMemo は処理に時間がかかる関数の実行結果をキャッシュしておくのに使います
ここで useMemo に渡している関数はこれです

() => <Child a={a} />

これを実行しても createElement で Child コンポーネントを作るだけ
この段階では Child コンポーネントのレンダリングは実行されないので Child 関数は実行されないはずです

const Heavy = ({ text }) => {
console.log(1)
const d = Date.now()
while (Date.now() - d < 1000) {}
return html`<div>Heavy: ${text}</div>`
}

const App = () => {
html`<${Heavy} />`
html`<${Heavy} />`
html`<${Heavy} />`
html`<${Heavy} />`
html`<${Heavy} />`

return html`
<div>
a
</div>
`
}

こういうコードを用意して実行してみました
Heavy はレンダリング処理に 1 秒かかります

しかしこのページは 1 秒もかからず表示されますし Heavy 関数内の console.log(1) は一度も実行されません

なので

<Child a={a} />

をメモしても特に意味がないように思います


とは言え公式ドキュメントなので間違っていたり古いというのも考えづらいので実行してみます

const Heavy = ({ text }) => {
const d = Date.now()
while (Date.now() - d < 1000) {}
return html`<div>Heavy: ${text}</div>`
}

const App = () => {
const [count, setCount] = useState(1)
const [text, setText] = useState("a")
return html`
<div>
<button onClick=${() => setCount(count + 1)}>${count}</button>
<${Heavy} text=${text} />
</div>
`
}
const Heavy = ({ text }) => {
const d = Date.now()
while (Date.now() - d < 1000) {}
return html`<div>Heavy: ${text}</div>`
}

const App = () => {
const [count, setCount] = useState(1)
const [text, setText] = useState("a")
const child = useMemo(() => html`<${Heavy} text=${text} />`, [text])

return html`
<div>
<button onClick=${() => setCount(count + 1)}>${count}</button>
${child}
</div>
`
}

Heavy コンポーネントはさっきと同じで 1 秒間かかるものです
上側が useMemo なしで 下側が useMemo ありの場合です

上の場合は初回表示とボタンを押して App コンポーネントの再レンダリングが起きるたびに 1 秒かかります
しかし 下側では App コンポーネントの再レンダリングは一瞬でした
ちゃんとメモできてるようです

この結果を見るに createElement で作られたオブジェクトに 後からレンダリングされた結果が含まれるみたいです
これまで createElement で作られるオブジェクトは 子コンポーネントの中身は含まれず JSX などで書いた DOM 定義を JavaScript のオブジェクト表現で表しただけのものと思っていました
値は immutable なオブジェクトで気軽に使い回せるものだと思ってたのですが 違ったようです
「<Child />」 だけでも その子孫コンポーネントすべてを含めた最終的な DOM のツリー全体が保持されているなら 思ってたほど気軽に使い回して良いものじゃないかもしれません
予期しない変な問題が出るかもです
ですが type と props が同じなら毎回レンダリングをしたところで同じ結果になるはず(なるように作るべき)なので気にしなくていいと言えばいいのかもしれません

とりあえず

<Child a={1} />



{ type: Child, props: {a: 1} }

みたいな単純変換しただけのオブジェクトになるのではなく 子孫コンポーネントのレンダリング結果まで保持されてるとは知っておいたほうが良さそうです

また この動きは React と Preact どっちも同じでした
React は複雑なのでシンプルな Preact でみてみると h (createElement) で作ったオブジェクトには _children や _dom などのプロパティがあり 子コンポーネントのレンダリング後にここに値がセットされていました