配列の一部だけを更新してre-renderを抑制する
たぶんできるはず
ここにコードを書いて検証してみる
state/propsの更新
これは知ってるtakker.icon
これを知らなかったtakker.icon
code:app.jsx
/** @jsx h */
/** @jsxFrag Fragment */
const Dial = ({ onClick, children }) => {
console.log("rendered!");
return (
<div style="user-select: none;" onClick={onClick}>
{children}
</div>
);
};
const App = () => {
const countUp = useCallback(() => setCount((prev) => prev + 1), []);
const countDown = useCallback(() => setCount((prev) => prev - 1), []);
return (
<div style="position: fixed; top:60px; left: 50%; transform: translate(-50%, 0); background-color: var(--page-bg, #fefefe); color: var(--page-text-color, #ccc); border: solid 1px rgba(0,0,0,.15); border-radius: 4px;"> Counter: {count}<br />
<Dial onClick={countUp}>+</Dial>
<Dial onClick={countDown}>-</Dial>
</div>
);
};
const div = document.createElement("div");
document.body.append(div);
render(<App />, div);
上のは配列ではなかった
配列を使ったサンプルを作ってみる
上述したuseMemo()でcomponentを包む方法を使ったら、値が変わったcomponentだけ再レンダリングされるようになった useMemo()で包まない場合、DOMの更新自体は値が変わったものだけ行われるが、値が変更されていないcomponent functionの呼び出しを抑制できない
差分検知方法
仕方ないのでconsole出力で結果を見ることにした
code:array.jsx
/** @jsx h */
/** @jsxFrag Fragment */
import { Fragment, h, render } from "../preact/mod.tsx";
import { useState, useEffect, useMemo } from "../preact/hooks.ts";
const Item = ({ index, children }) => useMemo(() => {
console.debug([${index}] rendered!);
return (
<div className="item">
{children}
</div>
);
const rand = () => Math.round(Math.random() * 1000000);
const App = () => {
// 同一性を確認するために、objectとして格納する
]);
データ更新部
code:array.jsx
useEffect(() => {
const timer = setInterval(() => setNumbers((list) => {
// 元の配列を破壊せずに更新する
const index = Math.floor(Math.random() * list.length);
リストを破壊して返さないと、numbersが変更されなかったと判断されて、<App />がre-renderされなくなってしまうようだ
結果、子コンポーネントの表示も一切変わらなくなる
code:array.jsx
}), 1000);
return () => clearInterval(timer);
}, []);
code:array.jsx
return (<>
<style>{`
.item {
user-select: none;
}
.container {
position: fixed;
top: 60px;
left: 50%;
transform: translate(-50%, 0);
color: var(--page-text-color, #ccc); border: solid 1px rgba(0,0,0,.15);
border-radius: 4px;
}
`}</style>
<div className="container">
{numbers.map((n, i) => (<Item key={i} index={i}>{n}</Item>))}
</div>
</>);
};
const div = document.createElement("div");
const root = div.attachShadow({ mode: "open" });
document.body.append(div);
render(<App />, root);