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

使い方

ツリーの深い部分にデータを渡す

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

import { useContext } from 'react';

function Button() {
const theme = useContext(ThemeContext);
// ...

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

context を Button に渡すために、それを対応する context プロバイダでラップします :

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

function Form() {
// ... ボタンを内側にレンダーします ...
}

プロバイダと 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 を context value としてプロバイダに渡します。

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 値を受け取ります。プロバイダに渡す theme 値を更新するために setTheme を呼び出すと、すべての Button コンポーネントが新しい 'light' 値で再レンダーされます。

Examples of updating context

1/5:
contextを介して値を更新する

この例では、MyApp コンポーネントが state 変数を保持し、それを ThemeContext プロバイダに渡します。"Dark mode"のチェックボックスをチェックすると、state が更新されます。提供される値を変更すると、context を使用しているすべてのコンポーネントが再レンダーされます。

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 curly braces. で渡します。波括弧を使うと、文字列でないコンテクスト値も渡すことができます。


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

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


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

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

<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 なしのプロバイダがある可能性があります:

// 🚩 動作しません:値プロパティがないからです
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>

value を指定するのを忘れると、value={undefined}を渡すのと同じです。

また、誤って別のプロップ名を使用した可能性もあります:

// 🚩 動作しません:プロパティは"value"と呼ばれるべきです
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>

これらのどちらの場合も、React からコンソールに警告が表示されるはずです。それらを修正するには、プロパティを value と呼びます:

// ✅ valueプロパティを渡す
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>

あなたが createContext(defaultValue)呼び出しからのデフォルト値 は、全く一致するプロバイダが存在しない場合にのみ使用されます。もし親ツリーのどこかに <SomeContext.Provider value={undefined}> コンポーネントがある場合、useContext(SomeContext) を呼び出すコンポーネントはコンテクスト値として undefined を受け取ります。