パフォーマンス
React
memo・lazy・Suspense・Profiler
React.memo / lazy / Suspense
コンポーネントの遅延読み込みとメモ化
import { memo, lazy, Suspense } from 'react';
// React.memo: props が変わらなければ再レンダリングをスキップ
const HeavyChart = memo(function HeavyChart({ data, config }) {
return <canvas>{/* 重い描画処理 */}</canvas>;
});
// カスタム比較関数
const Chart = memo(HeavyChart, (prev, next) => {
// true を返すと再レンダリングをスキップ
return prev.data === next.data && prev.config.color === next.config.color;
});
// lazy: コンポーネントを動的インポート(コード分割)
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
// Suspense: ローディング状態の表示
function App() {
return (
<Suspense fallback={<PageSkeleton />}>
<Dashboard />
</Suspense>
);
}
// ルートごとに分割
function Router() {
return (
<Suspense fallback={<Spinner />}>
{route === '/dashboard' && <Dashboard />}
{route === '/settings' && <Settings />}
</Suspense>
);
}useTransition / useDeferredValue
UIのブロッキングを防ぐ優先度制御
import { useState, useTransition, useDeferredValue } from 'react';
// useTransition: 重い更新を低優先度にする
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setQuery(e.target.value); // 高優先度(入力は即座に反映)
startTransition(() => {
// 低優先度(検索結果の更新は遅延可)
setResults(heavySearch(e.target.value));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<ResultList results={results} />
</>
);
}
// useDeferredValue: 値の更新を遅延
function FilteredList({ query }) {
// query の更新を遅延させて UI をブロックしない
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.7 : 1 }}>
<HeavyFilteredList query={deferredQuery} />
</div>
);
}