Next.jsでuseMemoやuseCallbackを使ったパフォーマンスの最適化
Next.js のパフォーマンスの低下は不要なレンダリングと不要な負荷の高い処理がの主な要因になりがちです、
対策として useMemo や useCallback などのフックスを使用することで、不要なレンダリングを防ぎ、
パフォーマンスを最適化することができます。
今回はその例を紹介します。
1. useMemo の使用例
リストのフィルタリングを行う際に、useMemo を使用して最適化します。
interface Props {
items: string[];
filter: string;
}
const FilteredList: React.FC<Props> = ({ items, filter }) => {
const filteredItems = useMemo(() => {
return items.filter((item) => item.includes(filter));
}, [items, filter]);
return (
<ul>
{filteredItems.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
};
この FilteredList コンポーネントでは、与えられた items の配列から、filter 文字列を含むアイテムのみをフィルタリングして表示します。
useMemo を使わない場合、このコンポーネントの任意のプロパティが変更されるたび(たとえ filter が変更されなくても)、items.filter() 関数が再実行されることになります。
これは特に items の配列が大きい場合にはパフォーマンス上のオーバーヘッドとなる可能性があります。
useMemo を使用することで、この高コストなフィルタリング操作を最適化できます。
具体的には、items または filter のいずれかが変更されたときのみ items.filter() 関数が再実行されるようになります。
この変更がない限り、useMemo は最後にメモ化された値(前回のフィルタリング結果)を返します。
結果として、items の配列が大きくても、filter に変更がない限りフィルタリングは再実行されません。
これにより、不要な計算を避けてパフォーマンスを最適化することができます。
2. useCallback の使用例
子コンポーネントに関数を props として渡す場面で useCallback を使用します。
interface Item {
id: number;
name: string;
}
interface Props {
items: Item[];
}
const ListWithButton: React.FC<Props> = ({ items }) => {
const handleItemClick = useCallback((itemId: number) => {
console.log(`Item ${itemId} was clicked`);
}, []);
return (
<div>
{items.map((item) => (
<button key={item.id} onClick={() => handleItemClick(item.id)}>
{item.name}
</button>
))}
</div>
);
};
この ListWithButton コンポーネントでは、各アイテムに対してボタンが表示され、
そのボタンをクリックすると handleItemClick 関数が実行されます。
useCallback を使用しない場合、このコンポーネントが再レンダリングされるたびに handleItemClick 関数が新しく生成されます。
これが問題となるシチュエーションは、特に handleItemClick 関数を子コンポーネントに props として渡す場合です。
関数が再生成されると、子コンポーネントは受け取る props が変更されたと判断し、不要に再レンダリングされる可能性があります。
useCallback を使用すると、handleItemClick 関数は一度だけ生成され、その後の再レンダリングで再生成されることはありません。
依存配列に変数を指定することで、その変数の値が変わったときのみ関数を再生成することができますが、
上記の例では依存する変数がないため、空の配列が指定されています。
このように useCallback を使用することで、不要な関数の再生成を防ぎ、子コンポーネントの不要な再レンダリングも防ぐことができ、
アプリケーションのパフォーマンスを最適化することができます。
パフォーマンス最適化の注意点
過度な最適化を避ける:
すべての関数や値をメモ化する必要はなく、
過度に最適化すると逆にパフォーマンスの低下を招くことがあります。
プロファイラの利用:
React DevTools のプロファイラを使用して、アプリケーションの実際のパフォーマンス問題を特定しボトルネックを特定してから最適化を行うことをおすすめします。
まとめ
Next.js でのパフォーマンス最適化は useMemo や useCallback を適切に使用することで、不要な再レンダリングを防ぎ、
ユーザーエクスペリエンスの向上を図ることができます。
しかし、適切な場所でのみこれらのフックを使用し、過度な最適化を避けることが重要です。
Discussion