useContext
は、コンポーネントからコンテキストを読み取り、購読できるReact Hookです。
const value = useContext(SomeContext)
リファレンス
useContext(SomeContext)
コンポーネントのトップレベルでuseContext
を呼び出して、コンテキストを読み取り、購読します。
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
パラメータ
SomeContext
:createContext
で事前に作成したコンテキスト。コンテキスト自体は情報を保持せず、コンポーネントに提供または読み取りできる情報の種類を表すだけです。
戻り値
useContext
は、呼び出し元のコンポーネントのコンテキスト値を返します。これは、ツリー内で呼び出し元のコンポーネントの上にある最も近い SomeContext.Provider
に渡された value
として決定されます。そのようなプロバイダーがない場合、返される値は、そのコンテキストの createContext
に渡した defaultValue
になります。返される値は常に最新です。React は、コンテキストが変更された場合、そのコンテキストを読み取るコンポーネントを自動的に再レンダリングします。
注意事項
- コンポーネント内の`useContext()`呼び出しは、*同じ*コンポーネントから返されたプロバイダの影響を受けません。対応する`
`は、`useContext()`呼び出しを行うコンポーネントの**_上_に配置する必要があります**。 - React は、特定のコンテキストを使用するすべての子要素を、異なる`value`を受け取るプロバイダから開始して**自動的に再レンダリング**します。以前の値と次の値は、`Object.is`比較を使用して比較されます。 `memo`を使用して再レンダリングをスキップしても、子要素が新しいコンテキスト値を受け取るのを防ぐことはできません。
- ビルドシステムが出力に重複したモジュールを生成する場合(シンボリックリンクで発生する可能性があります)、これはコンテキストを壊す可能性があります。コンテキストを介して何かを渡すことは、コンテキストを提供するために使用する`SomeContext`と、それを読み取るために使用する`SomeContext`が、`===`比較によって決定されるように、**_完全に_同じオブジェクト**である場合にのみ機能します。
使用方法 (SVGアイコン)
ツリーの深部にデータを渡す (SVGアイコン)
コンポーネントのトップレベルでuseContext
を呼び出して、コンテキストを読み取り、購読します。
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
// ...
`useContext`は、渡したコンテキストのコンテキスト値を返します。コンテキスト値を決定するために、React はコンポーネントツリーを検索し、その特定のコンテキストの**最も近い上位のコンテキストプロバイダ**を見つけます。
コンテキストを`Button`に渡すには、`Button`またはその親コンポーネントのいずれかを対応するコンテキストプロバイダでラップします。
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}
プロバイダと`Button`の間にいくつのコンポーネント層があるかは関係ありません。 `Form`内の*どこでも*`Button`が`useContext(ThemeContext)`を呼び出すと、値として` "dark"`を受け取ります。
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
コンテキストを介して渡されるデータの更新 (SVGアイコン)
多くの場合、コンテキストを時間の経過とともに変更したいと思うでしょう。コンテキストを更新するには、ステートと組み合わせます。親コンポーネントで状態変数を宣言し、現在の状態をコンテキスト値としてプロバイダに渡します。
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}
これで、プロバイダ内の`Button`は現在の`theme`値を受け取ります。 `setTheme`を呼び出して、プロバイダに渡す`theme`値を更新すると、すべての`Button`コンポーネントが新しい`'light'`値で再レンダリングされます。
コンテキスト更新の例 (SVGアイコン)
例 1〜の 5: コンテキストを介した値の更新 (SVGアイコン)
この例では、`MyApp`コンポーネントは状態変数を保持し、それが`ThemeContext`プロバイダに渡されます。「ダークモード」チェックボックスをオンにすると、状態が更新されます。提供される値を変更すると、そのコンテキストを使用するすべてのコンポーネントが再レンダリングされます。
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={theme}> <Form /> <label> <input type="checkbox" checked={theme === 'dark'} onChange={(e) => { setTheme(e.target.checked ? 'dark' : 'light') }} /> Use dark mode </label> </ThemeContext.Provider> ) } function Form({ children }) { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
value="dark"
は文字列 "dark" を渡しますが、value={theme}
は JSX 中括弧 を使用して JavaScript の theme
変数の値を渡すことに注意してください。中括弧を使用すると、文字列以外のコンテキスト値を渡すこともできます。
フォールバックのデフォルト値の指定
React が親ツリー内で特定のコンテキストのプロバイダーを見つけられない場合、useContext()
によって返されるコンテキスト値は、コンテキストを作成したときに指定したデフォルト値と同じになります。
const ThemeContext = createContext(null);
デフォルト値は変更されません。コンテキストを更新する場合は、上記のように状態と共に使用してください。
多くの場合、null
の代わりに、デフォルトとして使用できるより意味のある値があります。たとえば、
const ThemeContext = createContext('light');
こうすることで、対応するプロバイダーなしで誤ってコンポーネントをレンダリングしても、壊れることはありません。また、テスト環境で多くのプロバイダーを設定することなく、コンポーネントを適切に動作させることができます。
以下の例では、「テーマの切り替え」ボタンは常に明るいテーマです。これは、ボタンがテーマコンテキストプロバイダーの外部にあり、デフォルトのコンテキストテーマ値が 'light'
であるためです。デフォルトのテーマを 'dark'
に編集してみてください。
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext('light'); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <> <ThemeContext.Provider value={theme}> <Form /> </ThemeContext.Provider> <Button onClick={() => { setTheme(theme === 'dark' ? 'light' : 'dark'); }}> Toggle theme </Button> </> ) } function Form({ children }) { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children, onClick }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className} onClick={onClick}> {children} </button> ); }
ツリーの一部のコンテキストをオーバーライドする (SVGアイコン)
ツリーの一部を異なる値を持つプロバイダーでラップすることにより、その部分のコンテキストをオーバーライドできます。
<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>
プロバイダーは必要に応じてネストしたりオーバーライドしたりできます。
コンテキストのオーバーライドの例 (SVGアイコン)
例 1〜の 2: テーマのオーバーライド (SVGアイコン)
ここでは、Footer
の内部にあるボタンは、外部のボタン ("dark"
) とは異なるコンテキスト値 ("light"
) を受け取ります。
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> <ThemeContext.Provider value="light"> <Footer /> </ThemeContext.Provider> </Panel> ); } function Footer() { return ( <footer> <Button>Settings</Button> </footer> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> {title && <h1>{title}</h1>} {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
オブジェクトと関数を渡すときの再レンダリングの最適化 (SVGアイコン)
コンテキストを介して、オブジェクトや関数を含むあらゆる値を渡すことができます。
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}
return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}
ここでは、コンテキスト値は、2つのプロパティを持つ JavaScript オブジェクトであり、そのうちの1つは関数です。MyApp
が再レンダリングされるたびに(たとえば、ルートの更新時)、これは異なる関数を指す異なるオブジェクトになるため、React は useContext(AuthContext)
を呼び出すツリーの深い部分にあるすべてのコンポーネントも再レンダリングする必要があります。
小規模なアプリでは、これは問題ではありません。ただし、currentUser
などの基礎となるデータが変更されていない場合、それらを再レンダリングする必要はありません。React がその事実を活用できるように、login
関数を useCallback
でラップし、オブジェクトの作成を useMemo
でラップすることができます。これはパフォーマンスの最適化です。
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}
この変更の結果、MyApp
を再レンダリングする必要がある場合でも、currentUser
が変更されない限り、useContext(AuthContext)
を呼び出すコンポーネントを再レンダリングする必要はありません。
useMemo
と useCallback
についての詳細をご覧ください。
トラブルシューティング (SVGアイコン)
プロバイダからの値がコンポーネントで認識されません
これには、いくつかの一般的な原因が考えられます。
useContext()
を呼び出しているコンポーネントと同じコンポーネント(またはその下)で<SomeContext.Provider>
をレンダリングしています。<SomeContext.Provider>
を、useContext()
を呼び出しているコンポーネントの*上かつ外側*に移動してください。- コンポーネントを
<SomeContext.Provider>
でラップするのを忘れているか、ツリーの想定とは異なる部分に配置している可能性があります。React DevTools を使用して、階層が正しいかどうかを確認してください。 - ツールにビルドの問題があり、プロバイダコンポーネントから見える
SomeContext
と、読み取りコンポーネントから見えるSomeContext
が2つの異なるオブジェクトになる可能性があります。これは、例えばシンボリックリンクを使用している場合に発生する可能性があります。これを確認するには、それらをwindow.SomeContext1
やwindow.SomeContext2
のようなグローバル変数に割り当て、コンソールでwindow.SomeContext1 === window.SomeContext2
かどうかを確認します。同じでない場合は、ビルドツールのレベルでその問題を修正してください。
デフォルト値が異なるにもかかわらず、コンテキストから常に undefined
が返されます
ツリーに value
がないプロバイダがある可能性があります。
// 🚩 Doesn't work: no value prop
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>
value
を指定するのを忘れると、value={undefined}
を渡すのと同じです。
誤って別のプロパティ名を使用している可能性もあります。
// 🚩 Doesn't work: prop should be called "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>
どちらの場合も、コンソールに React からの警告が表示されるはずです。修正するには、プロパティ value
を呼び出してください。
// ✅ Passing the value prop
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>
createContext(defaultValue)
呼び出しのデフォルト値は、**上に一致するプロバイダが全くない場合にのみ**使用されることに注意してください。親ツリーのどこかに <SomeContext.Provider value={undefined}>
コンポーネントがある場合、useContext(SomeContext)
を呼び出しているコンポーネントはコンテキスト値として undefined
を受け取ります。