UseContextの基本的な使い方
参考
はじめに
ReactのContextAPIは、Reactアプリケーション全体でデータを簡単に共有するための機能を提供しています。特に、プロップスドリリング(深い階層のコンポーネント間でプロップスを渡すこと)の問題を解決するのに役立ちます。この記事では、ContextAPIの基本的な使用方法と、stateと更新関数を分割したContextを利用した実践的な例を通じてその利点を説明します。
ContextAPIの基本的な使用方法
1. Contextの作成と分割
まず、必要なデータごとにContextを作成します。ここでは、UserStateContext
とUserDispatchContext
を作成します。
import React, { createContext, useState, useContext } from 'react';
// State用のContextを作成します
const UserStateContext = createContext();
// 更新関数用のContextを作成します
const UserDispatchContext = createContext();
2. Context Providerの定義
次に、各ContextのProvider
を作成し、共有したい値を提供します。
// UserProviderコンポーネントを定義します
const UserProvider = ({ children }) => {
const [user, setUser] = useState({ name: 'John Doe', age: 30 });
return (
<UserStateContext.Provider value={user}>
<UserDispatchContext.Provider value={setUser}>
{children}
</UserDispatchContext.Provider>
</UserStateContext.Provider>
);
};
// 各Contextを利用するカスタムフックを定義してエクスポートします
const useUserState = () => useContext(UserStateContext);
const useUserDispatch = () => useContext(UserDispatchContext);
export { UserProvider, useUserState, useUserDispatch };
3. Contextの使用
次に、useUserState
とuseUserDispatch
を使用して、必要なコンポーネントでContextの値にアクセスします。<button>
部分をコンポーネント化し、更新関数だけを参照するコンポーネントにします。
import React from 'react';
import { useUserState, useUserDispatch } from './UserProvider';
const UserProfile = () => {
const user = useUserState();
return (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
<IncreaseAgeButton />
</div>
);
};
const IncreaseAgeButton = () => {
const setUser = useUserDispatch();
return (
<button onClick={() => setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }))}>
Increase Age
</button>
);
};
export default UserProfile;
4. アプリケーションにProviderを追加
最後に、アプリケーションにProviderを追加します。
import React from 'react';
import ReactDOM from 'react-dom';
import { UserProvider } from './UserProvider';
import UserProfile from './UserProfile';
const App = () => (
<UserProvider>
<UserProfile />
</UserProvider>
);
ReactDOM.render(<App />, document.getElementById('root'));
コンポーネントの再レンダリングについて
ContextAPIを使用する際の懸念の一つは、コンテキストの値が変更されるたびにコンシューマーコンポーネント[1]が再レンダリングされることです。これがパフォーマンスに悪影響を与えることがあります。stateと更新関数を分割したContextを利用することで、必要な部分だけが再レンダリングされるようにすることができます。
例えば、上記のコードでは<IncreaseAgeButton>
コンポーネントは更新関数だけを参照しており、stateの変更に依存しません。そのため、ユーザー情報が変更されても<IncreaseAgeButton>
コンポーネントは再レンダリングされません。
再レンダリングの防止策
-
useCallback/useMemo
を使用し、コールバック関数や計算結果をメモ化して、不要な再レンダリングを防ぎます。 - stateと更新関数を分割したContextを利用して、特定の部分だけが再レンダリングされるようにします。 (今回の例)
下記はuseCallback
とuseMemo
を使用して再レンダリングを最適化する方法です。
import React, { useCallback, useMemo } from 'react';
import { useUserState, useUserDispatch } from './UserProvider';
const UserProfile = () => {
const user = useUserState(); // UserStateContextからユーザー情報を取得
// userが変更されるたびに新しいJSX要素を計算します
const renderedUser = useMemo(() => (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
), [user]); // userが変更された場合のみ再計算
return (
<div>
{renderedUser} {/* メモ化されたユーザー情報をレンダリング */}
<IncreaseAgeButton /> {/* IncreaseAgeButtonコンポーネントをレンダリング */}
</div>
);
};
const IncreaseAgeButton = () => {
const setUser = useUserDispatch(); // UserDispatchContextからユーザー情報を更新する関数を取得
// setUserが変更されない限り新しいコールバック関数を作成しません
const handleIncreaseAge = useCallback(() => {
setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
}, [setUser]); // setUserが変更された場合のみ再作成
return (
<button onClick={handleIncreaseAge}>
Increase Age
</button>
);
};
export default UserProfile;
まとめ
ReactのContextAPIは、アプリケーション全体でデータを簡単に共有するのに非常に便利です。stateと更新関数を分割したContextを利用することで、特定のデータに依存するコンポーネントのみが再レンダリングされるようにすることができ、パフォーマンスの最適化が図れます。特に、更新関数だけを参照するコンポーネントは、stateの変更に影響されず再レンダリングされないため、効率的なレンダリングが可能です。
-
コンテキストを利用しているコンポーネント。今回でいう
UserProfile
とIncreaseAgeButton
を指します。 ↩︎
Discussion