React でのコンポーネントのパフォーマンス最適化には、不必要な再レンダリングを防ぐことがキーポイントです。
ここでは、React の memo
と useCallback
フックを使用して、効率的なコンポーネント更新を実現する方法を紹介します。
コンポーネントの再レンダリングされる条件
-
コンポーネントの state が更新された時
-
渡されている props の値が変更された時
-
親コンポーネントが再レンダリングされた時
→ メモ化によって不要な再レンダリングをスキップできる
memo
再レンダリングをスキップできるケース
再レンダリングされた親コンポーネントから渡されている props が変更されていない場合。
通常は親コンポーネントが再レンダリングされると、必ず子コンポーネントも再レンダリングされます。
しかし、memo
関数は新しい props が古い props と同じである限り再レンダリングされないコンポーネントを作ります。
これを "メモ化されたコンポーネント" と呼びます。
使い方
コンポーネントを memo
関数でラップする。
import { memo } from "react";
const UserProfile = memo((props) => {
const { user } = props;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
});
export default UserProfile;
推奨されるシナリオ
- レンダリングコストの高いコンポーネント:
大きなリスト、複雑な状態を持つフォームフィールドなど、再レンダリングのコストが特に高いコンポーネントは、props が変更されない限り、再レンダリングを避けるためにメモ化が有効です。
- 静的コンポーネント:
再レンダリングがほぼ発生しない、静的な情報を表示するコンポーネント(例:ヘッダーやフッターなど)も、メモ化によって初期レンダリングのみを行い、以降の無駄な処理を省くことができます。
useCallback
useCallback
はメモ化されたコンポーネントに props として、関数を渡す際に有効なフックです。
memo
関数と併用することで不要な再レンダリングを防ぐことができるということです。
メモ化だけでは再レンダリングを防げないケース
メモ化されたコンポーネントは、渡されている props が変更されなければ基本的に再レンダリングされません。
しかし、props が変わってなくても親コンポーネントの再レンダリングによって、再レンダリングされてしまうケースがあります。
それが関数を props として受け取っているケースです。
JavaScriptでは関数もオブジェクトの一種であるため、その関数の参照先が重要になります。
親コンポーネントの再レンダリングごとに新しいインスタンスが生成され、これが React に新しい props が渡されたとみなされてしまうため、子コンポーネントも再レンダリングされてしまいます。
関数のメモ化
useCallback
フックは関数をメモ化します。メモ化された関数は依存するデータが変更されない限り、同じ参照を返します。
これにより、上記の問題を回避することができます。
使い方
useCallback
は、第一引数にメモ化したい関数を、第二引数にその関数内で依存している変数の配列(依存配列)を指定します。
依存配列内のどれかの値が変わると、関数は再生成されます。
もし依存配列を空にすると、コンポーネントがマウントされた時のみ関数が生成され、アンマウントされるまで同じ関数が使い回されます。
// 子コンポーネント
import { memo } from "react";
// ChildComponent をメモ化
const ChildComponent = memo((props) => {
const { onClick } = props;
return <button onClick={onClick}>Click</button>;
});
export default ChildComponent;
// 親コンポーネント
import { useState, useCallback } from "react";
const ParentComponent = () => {
const [count, setCount] = useState(0);
// increment 関数をメモ化して、依存配列中の setCount が変わらない限り関数が再生成されないようにする
const increment = useCallback(() => {
setCount((prev) => prev + 1)
}, [setCount]); // 依存配列に setCount を追加
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={increment} />
</div>
);
};
export default ParentComponent;
まとめ
memo
と useCallback
を適切に使用することで、React アプリケーションのパフォーマンスを向上させることが可能です。
これらのツールは、特に大規模なアプリケーションや高頻度で更新が行われる UI コンポーネントにおいて、その効果を発揮します。
適切な依存配列を設定し、不必要な再計算や再レンダリングを避けることで、スムーズで応答性の高いユーザー体験を提供できます。