useContext はコンポーネントから context を読み取り、サブスクライブするための React のフックです。

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() 呼び出しは、同じコンポーネントから返されるプロバイダの影響を受けません。該当する <Context.Provider> は、useContext()を呼び出したコンポーネントの上にある必要があります。
  • 特定のコンテクストを使用する全ての子コンポーネントは、異なる value を受け取るプロバイダから始まり、React によって自動的に再レンダーします。前の値と次の値は、Object.is で比較されます。memo で再レンダーをスキップしても、子のプロバイダは新しいコンテクスト値を受け取ることはありません。
  • ビルドシステムから生成されたアウトプットの中にモジュールの重複があったら、(シンボリックリンクで起こり得る場合がある)コンテクストを壊す可能性があります。コンテクストを介して何かを渡すことは、コンテクストを提供するために使用する SomeContext と、読み込むために使用する SomeContext が、=== 比較によって決定されるように、正確に同じオブジェクトである場合にのみ動作します。

使い方

ツリーの深くにデータを渡す

コンポーネントのトップレベルで useContext を呼び出して コンテクスト を読み取り、サブスクライブします。

import { useContext } from 'react'; function Button() { const theme = useContext(ThemeContext); // ...

useContextコンテクストの値渡したコンテクスト のために返します。コンテクストの値を決定するために、React はコンポーネントツリーを探索し、特定のコンテクストに対して最も近い上位のコンテクストプロバイダを見つけます。

コンテクストを Button に渡すために、該当のコンテクストプロバイダでラップします :

function MyPage() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ); } function Form() { // ... renders buttons inside ... }

プロバイダと Button の間にどれだけ多くのコンポーネントの層があっても関係ありません。Form の内部のどこかButtonuseContext(ThemeContext) を呼び出すとき、値として"dark"を受け取ります。

落とし穴

useContext() は、呼び出すコンポーネントより最も近い上位にあるプロバイダを常に探します。上位に検索し、useContext() を呼び出したコンポーネントにあるプロバイダは考慮しません

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> ); }

コンテクストを介したデータの更新

多くの場合、時間とともにコンテクストを変化させたいと思うでしょう。コンテクストを更新するために、それを state. と組み合わせてください。親コンポーネントで state 変数を宣言します。親コンポーネントで state 変数を宣言して、現在の state を コンテクストの値 としてプロバイダに渡します。

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' 値で再レンダーされます。

Examples of updating context

例 1/5:
コンテクストを介して値を更新する

この例では、MyApp コンポーネントが state 変数を保持し、それが 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} は JavaScript の theme 変数の値を JSX の中括弧 で渡しすことに注意してください。中括弧を使うことで、文字列以外のコンテクスト値も渡すことができます。


フォールバックのデフォルト値の指定

React が特定のコンテクストのプロバイダを親ツリーで見つけれたら、useContext() が返すコンテクストの値は、コンテキストを作成したときに指定したデフォルト値と等しくなります:

const ThemeContext = createContext(null);

デフォルト値は絶対に変更されません。コンテクストを更新したいなら、上記で説明したように、state と一緒に使用します。

多くの場合、null の代わりにデフォルトとして意味のある値を使います。例えば :

const ThemeContext = createContext('light');

このようにすると、間違って該当のプロバイダがない状況でコンポーネントをレンダーしても、壊れることはありません。テスト環境で多くのプロバイダを設定しなくても、コンポーネントが適切に動作するのに役立ちます。

下記の例では、「テーマの切り替え」ボタンは常に 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> ); }

ツリーにある一部のコンテクストを上書きする

ツリーにある異なる値を持つプロバイダでラップすることにより、一部のコンテクストを上書きできます。

<ThemeContext.Provider value="dark"> ... <ThemeContext.Provider value="light"> <Footer /> </ThemeContext.Provider> ... </ThemeContext.Provider>

必要な回数だけ、プロバイダをネストして上書きすることができます。

例を試す

例 1/2:
テーマの上書き

この例では、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> ); }

オブジェクトと関数を渡すときの再レンダーの最適化

あなたはコンテクストを介して任意の値を渡すことができます、オブジェクトや関数を含みます。

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> ); }

ここでは、context value は 2 つのプロパティを持つ JavaScript オブジェクトで、そのうちの 1 つは関数です。MyApp が再レンダーされるたびに(例えば、ルートの更新時など)、これは異なるオブジェクトを指し、異なる関数を指しますので、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) を呼び出すコンポーネントを再レンダーする必要はありません。

useMemouseCallback についてもっと読むことができます。


トラブルシューティング

マイコンポーネントはプロバイダからの値を見ることができません

これが起こる一般的な方法はいくつかあります:

  1. useContext() を呼び出している同じコンポーネント(またはそれ以下)で <SomeContext.Provider> をレンダーしています。useContext() を呼び出すコンポーネントの上と外側<SomeContext.Provider> を移動してください。
  2. <SomeContext.Provider> でコンポーネントをラップするのを忘れているかもしれません、または思っていたよりもツリーの異なる部分に置いているかもしれません。React DevTools を使用して階層が正しいか確認してください。
  3. 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 を受け取ります。