react-component-performance について
React 開発者向けに、UI の動作を変えずにコンポーネントの描画遅延を特定し、最適化するスキルです。状態の分離やメモ化を行い、React DevTools Profiler で効果を検証します。
主な機能
- 01描画の要因とホットスポットの特定
- 02頻繁に変わる状態の分離
- 03props とコールバックの安定化
- 04計算量とリスト描画数の削減
- 05プロファイル記録の比較
活用シーン
- 01React の操作遅延の調査
- 02タイマー更新による大規模リストの再描画防止
- 03props が安定した行コンポーネントの最適化
- 04ベースラインとの比較による効果検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dimillian/ skills --skill react-component-performanceインストール後、Claude Code で/react-component-performanceを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlreferences/examples.md
詳細
React コンポーネントのパフォーマンス
概要
レンダーのホットスポットを特定し、コストの高い更新を切り分け、UI の動作を変えずに的を絞った最適化を適用します。
ワークフロー
- 動作の遅さを再現するか、内容を説明します。
- 再レンダーを引き起こす要因(状態の更新、props の頻繁な変更、エフェクト)を特定します。
- 頻繁に変わる状態を、負荷の高いサブツリーから切り離します。
- props とハンドラーを安定させ、効果が見込める箇所でメモ化します。
- コストの高い処理(計算、DOM のサイズ、リストの長さ)を減らします。
- 検証:React DevTools Profiler を開き、操作を記録して、16 ms を超えてレンダーされるコンポーネントをフレームグラフで確認します。最適化前の記録と比較します。
チェックリスト
- 計測する:React DevTools Profiler を使うか、レンダーをログに記録して、最適化前の基準を取得します。
- 頻繁な更新を見つける:タイマー、スクロール、入力、アニメーションによって更新される状態を特定します。
- 分割する:更新頻度の高い状態を子コンポーネントに移し、負荷の高いリストを静的に保ちます。
- メモ化する:props が安定している場合に限り、リーフ行を
memoでラップします。 - props を安定させる:ハンドラーと派生値に
useCallback/useMemoを使います。 - レンダー内での派生処理を避ける:事前計算するか、メモ化したヘルパー内で計算します。
- リストのサイズを制御する:長いリストはウィンドウ化/仮想化し、非表示の項目をレンダーしないようにします。
- キー:安定したキーを使い、順序が変わる可能性がある場合はインデックスを避けます。
- エフェクト:依存配列を確認し、レンダーのたびに再実行されるエフェクトを避けます。
- スタイル/レイアウト:負荷の高いレイアウト処理の連続や、大きな Markdown/差分のレンダーに注意します。
最適化パターン
頻繁に更新される状態を切り離す
タイマーやアニメーションのカウンターを子コンポーネントに移し、親リストが更新のたびに再レンダーされないようにします。
// ❌ Before – entire parent (and list) re-renders every second
function Dashboard({ items }: { items: Item[] }) {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return (
<>
<Clock tick={tick} />
<ExpensiveList items={items} /> {/* re-renders every second */}
</>
);
}
// ✅ After – only <Clock> re-renders; list is untouched
function Clock() {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <span>{tick}s</span>;
}
function Dashboard({ items }: { items: Item[] }) {
return (
<>
<Clock />
<ExpensiveList items={items} />
</>
);
}
useCallback と memo でコールバックを安定させる
// ❌ Before – new handler reference on every render busts Row memo
function List({ items }: { items: Item[] }) {
const handleClick = (id: string) => console.log(id); // new ref each render
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
// ✅ After – stable handler; Row only re-renders when its own item changes
const Row = memo(({ item, onClick }: RowProps) => (
<li onClick={() => onClick(item.id)}>{item.name}</li>
));
function List({ items }: { items: Item[] }) {
const handleClick = useCallback((id: string) => console.log(id), []);
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
派生データはレンダーの外で処理する
// ❌ Before – recomputes on every render
function Summary({ orders }: { orders: Order[] }) {
const total = orders.reduce((sum, o) => sum + o.amount, 0); // runs every render
return <p>Total: {total}</p>;
}
// ✅ After – recomputes only when orders changes
function Summary({ orders }: { orders: Order[] }) {
const total = useMemo(() => orders.reduce((sum, o) => sum + o.amount, 0), [orders]);
return <p>Total: {total}</p>;
}
その他のパターン
- 行を分割する:リストの行を、props を絞ったメモ化コンポーネントとして切り出します。
- 負荷の高いレンダーを遅延する:展開されるまで、コストの高いコンテンツを遅延レンダーするか折りたたんでおきます。
プロファイリングによる検証手順
- React DevTools → Profiler タブを開きます。
- Record をクリックし、遅い操作を実行してから Stop をクリックします。
- Flamegraph ビューに切り替えます。コンポーネント名と約 16 ms を超える時間が表示されているバーが候補です。
- Ranked chart を使って、自己レンダー時間の順に並べ替え、上位のボトルネックを対象にします。
- 一度に 1 つの最適化を適用して再記録し、レンダー回数と所要時間を最適化前の基準と比較します。
参考例
ユーザーが具体的なリファクタリング例を求めている場合は、references/examples.md を読み込みます。
コメント
Markdown 対応