Spec-Zone.ru › React 18

useContext

useContext — это React-хук, который позволяет вам читать и подписываться на контекст из вашего компонента.

const value = useContext(SomeContext)
  • Ссылка
    • useContext(SomeContext)
  • Использование
    • Передача данных глубоко в дерево
    • Обновление данных, переданных через контекст
    • Указание значения по умолчанию
    • Переопределение контекста для части дерева
    • Оптимизация повторных отрисовок при передаче объектов и функций
  • Отладка
    • Мой компонент не видит значение из моего поставщика
    • Я всегда получаю undefined из моего контекста, хотя значение по умолчанию отличается

Ссылка

useContext(SomeContext)

Вызовите useContext в верхней части вашего компонента, чтобы прочитать и подписаться на контекст.

import { useContext } from 'react';

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

См. примеры ниже.

Параметры

  • SomeContext: Контекст, который вы ранее создали с помощью createContext. Сам контекст не содержит информацию, он только представляет собой тип информации, которую можно предоставить или прочитать из компонентов.

Возвращаемое значение

useContext возвращает значение контекста для вызывающего компонента. Оно определяется как значение value , переданное ближайшему SomeContext.Provider над вызывающим компонентом в дереве. Если такого поставщика нет, то возвращаемое значение будет defaultValue, которое вы передали в createContext для этого контекста. Возвращаемое значение всегда актуальное. React автоматически перерисовывает компоненты, которые читают контекст, если он изменяется.

Ограничения

  • useContext() вызов в компоненте не зависит от поставщиков, возвращаемых из того же компонента. Соответствующий <Context.Provider> должен быть выше компонента, выполняющего вызов useContext().
  • React автоматически перерисовывает всех дочерних элементов, использующих определенный контекст, начиная с поставщика, который получает другое value. Предыдущие и последующие значения сравниваются с использованием сравнения Object.is. Использование memo для пропуска повторных отрисовок не предотвращает получение дочерними элементами новых значений контекста.
  • Если ваша система сборки генерирует дублируемые модули в выводе (что может произойти при использовании симлинков), это может нарушить контекст. Передача чего-либо через контекст работает только в том случае, если SomeContext , который вы используете для предоставления контекста, и SomeContext , который вы используете для его чтения, являются абсолютно одинаковыми объектами, как определяется сравнением ===.

Использование

Передача данных глубоко в дерево

Вызовите useContext в верхней части вашего компонента, чтобы прочитать и подписаться на контекст.

import { useContext } from 'react';

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

useContext возвращает значение контекста для контекста, который вы передали. Для определения значения контекста React ищет в дереве компонентов и находит ближайшего поставщика контекста выше для этого конкретного контекста.

Чтобы передать контекст в компонент, оберните его или один из его родительских компонентов в соответствующего поставщика контекста:

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

function Form() {
  // ... renders buttons inside ...
}

Не имеет значения, сколько слоев компонентов находится между поставщиком и Button. Когда Button где-либо внутри Form вызывает useContext(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>
  );
}


Обновление данных, переданных через контекст

Часто вы захотите, чтобы контекст менялся со временем. Для обновления контекста объедините его с состоянием. Объявите переменную состояния в родительском компоненте и передайте текущее состояние как значение контекста поставщику.

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'.


Указание значения по умолчанию

Если 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 получает другое значение контекста ("light") , чем кнопки снаружи ("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>
      <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>
  );
}

Здесь значение контекста — это JavaScript-объект с двумя свойствами, одно из которых — функция. Всякий раз, когда 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 нужно перерисовать, компоненты, вызывающие useContext(AuthContext) , не нужно перерисовывать, если currentUser не изменился.

Подробнее о useMemo и useCallback.


Отладка

Мой компонент не видит значение из моего поставщика

Есть несколько распространенных способов, как это может произойти:

  1. Вы отрисовываете <SomeContext.Provider> в том же компоненте (или ниже), что и вызов useContext(). Переместите <SomeContext.Provider> выше и вне компонента, вызывающего useContext().
  2. Вы, возможно, забыли обернуть свой компонент с помощью <SomeContext.Provider>, или вы могли поместить его в другую часть дерева, чем предполагали. Проверьте, правильно ли иерархия с помощью React DevTools.
  3. У вас могут возникнуть проблемы со сборкой с инструментами, которые приводят к тому, что SomeContext, как видно из компонента-поставщика, и SomeContext, как видно из компонента-читателя, являются двумя разными объектами. Это может произойти, если вы используете симлинки, например. Вы можете проверить это, присвоив их глобальным переменным, таким как 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 в качестве значения контекста.

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/react/useContext

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API