createContext
createContext
は、コンポーネントが提供または読み取りできるコンテクストを作成するための関数です。
const SomeContext = createContext(defaultValue)
リファレンス
createContext(defaultValue)
createContext
をコンポーネントの外部で呼び出してコンテクストを作成します。
import { createContext } from 'react';
const ThemeContext = createContext('light');
引数
defaultValue
: コンポーネントがコンテクストを読み取るときに、その上のツリー内で対応するコンテクストプロバイダがない場合にコンテクストが持つ値です。デフォルト値が必要ない場合はnull
を指定します。デフォルト値は「最後の手段」として使われるように意図されています。これは静的な値であり、時間が経過しても変化しません。
返り値
createContext
はコンテクストオブジェクトを返します。
コンテクストオブジェクト自体は情報を持っていません。他のコンポーネントがどのコンテクストを読み取るか、または提供するかを表します。通常、上位のコンポーネントで SomeContext.Provider
を使用してコンテクストの値を指定し、下位のコンポーネントで useContext(SomeContext)
を呼び出してコンテクストを読み取ります。コンテクストオブジェクトにはいくつかのプロパティがあります:
SomeContext.Provider
では、コンポーネントにコンテクストの値を提供できます。SomeContext.Consumer
は、コンテクストの値を読み取るための方法ですが、あまり使用されません。
SomeContext.Provider
コンポーネントをコンテクストプロバイダでラップすると、内部のコンポーネントに対してこのコンテクストの値を指定できます。
function App() {
const [theme, setTheme] = useState('light');
// ...
return (
<ThemeContext.Provider value={theme}>
<Page />
</ThemeContext.Provider>
);
}
props
value
: このプロバイダの内側(深さに関わらず)にあるコンポーネントがコンテクストを読み取る際に、渡したい値です。コンテクストの値は任意の型にすることができます。プロバイダ内でuseContext(SomeContext)
を呼び出しているコンポーネントは、それより上位かつ最も内側にある対応するコンテクストプロバイダのvalue
を受け取ります。
SomeContext.Consumer
useContext
が存在する前には、コンテクストを読み取る古い方法がありました:
function Button() {
// 🟡 Legacy way (not recommended)
return (
<ThemeContext.Consumer>
{theme => (
<button className={theme} />
)}
</ThemeContext.Consumer>
);
}
この古い方法はまだ動作しますが、新しく書かれたコードは useContext()
を使ってコンテクストを読み取るべきです:
function Button() {
// ✅ Recommended way
const theme = useContext(ThemeContext);
return <button className={theme} />;
}
props
children
: 関数です。React は、useContext()
と同じアルゴリズムによって定まる現在のコンテクスト値で関数を呼び出し、その関数から返される結果をレンダーします。親コンポーネントからのコンテクストが変更されると、React はこの関数を再実行し、UI を更新します。
使用法
コンテクストの作成
コンテクストを利用することで、明示的に props を渡さずに、コンポーネントに深くまで情報を渡すことができます。
コンポーネントの外部で createContext
を呼び出して、コンテクストを 1 つまたは複数個作成します。
import { createContext } from 'react';
const ThemeContext = createContext('light');
const AuthContext = createContext(null);
createContext
はコンテクストオブジェクトを返します。それを useContext()
に渡すことで、コンポーネントからコンテクストを読み取ることができます:
function Button() {
const theme = useContext(ThemeContext);
// ...
}
function Profile() {
const currentUser = useContext(AuthContext);
// ...
}
デフォルトでは、コンポーネントが受け取る値は、コンテクストを作成するときに指定したデフォルトの値になります。しかし、デフォルトの値は決して変わらないため、これ自体では役に立ちませんね。
コンテクストが便利なのは、コンポーネントから動的な値を提供できるからです:
function App() {
const [theme, setTheme] = useState('dark');
const [currentUser, setCurrentUser] = useState({ name: 'Taylor' });
// ...
return (
<ThemeContext.Provider value={theme}>
<AuthContext.Provider value={currentUser}>
<Page />
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
これで、Page
コンポーネントとその内部のすべてのコンポーネントは、どんなに深くても、渡されたコンテクストの値を「見る」ことができます。渡されたコンテクストの値が変更されると、React はコンテクストを読み取るコンポーネントを再レンダーします。
ファイルからのコンテクストのインポートとエクスポート
異なるファイルにあるコンポーネントが同じコンテクストにアクセスする必要があることがよくあります。そのため、一般的には別ファイルでコンテクストを宣言します。他のファイルでコンテクストを利用できるようにするために、export
文を使用できます:
// Contexts.js
import { createContext } from 'react';
export const ThemeContext = createContext('light');
export const AuthContext = createContext(null);
他のファイルで宣言されたコンポーネントは、import
文を使用して、このコンテクストを読み取ったり、提供したりすることができます:
// Button.js
import { ThemeContext } from './Contexts.js';
function Button() {
const theme = useContext(ThemeContext);
// ...
}
// App.js
import { ThemeContext, AuthContext } from './Contexts.js';
function App() {
// ...
return (
<ThemeContext.Provider value={theme}>
<AuthContext.Provider value={currentUser}>
<Page />
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
これはコンポーネントのインポートとエクスポートと同様に動作します。
トラブルシューティング
コンテクストの値を変更する方法が見つからない
このようなコードはデフォルトのコンテクストの値を指定します:
const ThemeContext = createContext('light');
この値は決して変わりません。React は、対応するプロバイダを上位のコンポーネントで見つけられない場合にのみ、この値をフォールバックとして使用します。
コンテクストを時間の経過とともに変化させるには、state を追加し、コンポーネントをコンテクストプロバイダでラップします。