frontend-review-performance について
Reactフロントエンドの描画性能を確認するスキルです。メモ化、大規模リスト、並行機能、描画構成を調べ、変更提案の前にプロファイリングを求めます。
主な機能
- 01メモ化の使用状況と妥当性の確認
- 02仮想スクロールのない大規模リストの特定
- 03並行レンダリング機能の活用機会の確認
- 04Contextによる再レンダリングの確認
- 05CanvasやWebGLとの描画分離の確認
活用シーン
- 01Reactの描画性能リスクをレビュー
- 02仮想化が必要になり得る大規模リストを特定
- 03最適化作業の前に計測を計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mizchi/ skills --skill frontend-review-performanceインストール後、Claude Code で/frontend-review-performanceを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
フロントエンドレビュー — レンダリングパフォーマンス
React フロントエンドのレンダリングパフォーマンスをレビューします。AI が生成する問題で特によくあるのは、計測せずに(またはまったく使わずに)あらゆる箇所に memo / useCallback / useMemo を適用すること、大きなリストで仮想スクロールを使わないこと、Context の変更によって無関係なコンポーネントが再レンダリングされることです。
手順
package.jsonでパフォーマンス関連のパッケージ(@tanstack/react-virtual、react-window、@welldone-software/why-did-you-renderなど)を確認します。- 既存の memo の使用箇所を grep します。
grep -rn "React\.memo\|useMemo\|useCallback" src/ --include='*.tsx' --include='*.ts' | wc -l grep -rn "useVirtualizer\|FixedSizeList\|VariableSizeList" src/ --include='*.tsx' | wc -l grep -rn "useTransition\|useDeferredValue\|startTransition" src/ --include='*.tsx' | wc -l - リストをレンダリングするコンポーネントのうち、最も規模の大きいものを見つけます(サイズ制限のない配列に対する
.map(を探します)。 - 頻繁に変更され、広範囲の再レンダリングを引き起こす可能性のある Context プロバイダーを探します。
iot-ops/ マップ / チャートアプリでは、負荷の高いレンダリングが React の state 内で行われているか、canvas/WebGL の ref 内で行われているかを確認します。
プロファイリング優先の原則
まずプロファイリングを行わずに、memo / useCallback / useMemo を推奨してはいけません。 時期尚早なメモ化は認知負荷を増やし、パフォーマンスを低下させることもあります(各フックにはコストがあります)。
レポートを作成する際は、最適化の推奨事項すべてに次の文言を付けてください。「プロファイリングで操作ごとに X 回再レンダリングされることが確認された場合は、Y を検討してください。」
メモ化の正確性
メモ化がすでに行われている場合(または推奨する場合)は、次のよくある間違いがないか確認します。
React.memo
React.memoは、親から安定した props を受け取るコンポーネントに適用されていますか?- 親がレンダリングのたびに新しいオブジェクト/配列/関数の参照を渡していませんか(それによりメモ化が無効になっていませんか)?
// Bad: new array on every render — memo is useless
<List items={data.filter(x => x.active)} />
// Good: stable reference with useMemo
const activeItems = useMemo(() => data.filter(x => x.active), [data]);
<List items={activeItems} />
useCallback
memoでラップされた子コンポーネントにコールバックを渡す際、useCallbackを使っていますか?- 依存配列は正確ですか(不足している依存や不要な依存はありませんか)?
// Bad: new function reference every render
<Button onClick={() => handleDelete(id)} />
// Good: stable reference
const handleDeleteClick = useCallback(() => handleDelete(id), [id, handleDelete]);
<Button onClick={handleDeleteClick} />
useMemo
useMemoは、単純な処理(文字列の連結、真偽値のチェック)ではなく、負荷の高い計算(大きな配列のフィルタリング/ソート/集計)に適用されていますか?- 依存配列は正しいですか?
仮想スクロール
100 件以上の項目を含むリストでは、許容できるパフォーマンスを得るために、ほぼ常に仮想スクロールが必要です。
推奨:@tanstack/react-virtual(あらゆるレイアウトで動作し、CSS に制約されません)。
const rowVirtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 48,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }}>
{rowVirtualizer.getVirtualItems().map(vItem => (
<div key={vItem.key} style={{ position: 'absolute', top: vItem.start, height: vItem.size }}>
<ListItem item={items[vItem.index]} />
</div>
))}
</div>
</div>
);
仮想スクロールを使わずに、100 件を超える配列を map しているリストにはフラグを立ててください。
並行機能(React 18 以降)
useTransition— 緊急性の低い負荷の高い state 更新をラップし、UI の応答性を保ちます。
const [isPending, startTransition] = useTransition();
const handleFilterChange = (q: string) => {
startTransition(() => setFilterQuery(q));
};
useDeferredValue— 負荷の高いレンダリングを引き起こす値を遅延させます。
const deferredQuery = useDeferredValue(filterQuery);
const filtered = useMemo(() => items.filter(i => i.name.includes(deferredQuery)), [deferredQuery, items]);
キー入力のたびにメインスレッドをブロックする負荷の高いフィルタリング/ソート処理にはフラグを立ててください。これらは useTransition の候補です。
Canvas / WebGL の分離(iot-ops / マップ / チャートアプリ)
データ密度の高い UI(リアルタイムダッシュボード、マップオーバーレイ、チャート表示)では、フレームごとの更新に React state を使うのは適切ではありません。
次の点を確認します。
- 高頻度のデータ(センサーの測定値、マップタイルの更新、チャートデータ)が React state を経由せず、
useRefを介して canvas/WebGL に直接渡されていますか? - React はレイアウトの外枠とコントロールパネルのみを制御し、canvas/WebGL レイヤーは独立してレンダリングを処理していますか?
// Pattern: React controls mount; canvas reads data via ref
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const renderer = new WebGLRenderer(canvasRef.current!);
const unsub = sensorStream.subscribe(data => renderer.update(data)); // no setState
return unsub;
}, []);
出力
<client-repo>/.frontend-review/report/latest/md/performance-review.md を作成し、次の内容を記載します。
- プロファイリングの推奨事項:最初に何を、どのように計測するか(React DevTools Profiler、why-did-you-render)
- メモ化の不足/誤用:各指摘のファイルと行番号
- 仮想スクロールの候補:コンポーネント名、推定リストサイズ
- 並行機能の適用機会:スレッドをブロックする操作
- Canvas/WebGL の評価(該当する場合):高頻度のデータが React state を経由していないか
- 推奨 PR:1 つの PR につき最適化を 1 つとし、PR の説明にプロファイリングのベンチマークを記載
200 行未満に収めてください。プロファイリングの根拠がない推奨事項には、「未確認 — まずプロファイリング」と明記してください。
範囲
- プロファイリングは実行しないでください。何をどのように計測するかを説明してください。
- 計測計画のない最適化を提案しないでください。
- クライアントリポジトリ内のソースファイルには手を加えないでください。
- 状態管理アーキテクチャ(ストア設計、セレクターの粒度)は
frontend-review-stateの対象です。
参考資料
- チェックリスト:
24-rendering-performance.md、23-state-management.md、C2-lighthouse.md - ツール:React DevTools Profiler、
@welldone-software/why-did-you-render、@tanstack/react-virtual