useContext é um Hook do React que permite que você leia e se inscreva em contexto a partir do seu componente.

const value = useContext(SomeContext)

Referência

useContext(SomeContext)

Chame useContext na raiz do seu componente para ler e se inscrever em contexto.

import { useContext } from 'react';

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

Veja mais exemplos abaixo.

Parâmetros

  • SomeContext: O contexto que você criou anteriormente com createContext. O contexto em si não contém as informações, ele apenas representa o tipo de informação que você pode fornecer ou ler a partir dos componentes.

Retornos

useContext retorna o valor do contexto para o componente chamador. Ele é determinado como o value passado para o mais próximo SomeContext.Provider acima do componente chamador na árvore. Se não houver tal provedor, o valor retornado será o defaultValue que você passou para createContext para esse contexto. O valor retornado está sempre atualizado. O React re-renderiza automaticamente os componentes que leem algum contexto se ele mudar.

Ressalvas

  • A chamada useContext() em um componente não é afetada pelos provedores retornados do mesmo componente. O correspondente <Context.Provider> precisa estar acima do componente que faz a chamada useContext().
  • O React re-renderiza automaticamente todos os filhos que usam um determinado contexto, começando do provedor que recebe um value diferente. Os valores anteriores e próximos são comparados com a comparação Object.is. Pular re-renderizações com memo não impede que os filhos recebam novos valores de contexto.
  • Se o seu sistema de build produzir módulos duplicados na saída (o que pode acontecer com symlinks), isso pode quebrar o contexto. Passar algo via contexto só funciona se SomeContext que você usa para fornecer o contexto e SomeContext que você usa para lê-lo são exatamente o mesmo objeto, conforme determinado por uma comparação ===.

Uso

Passando dados profundamente na árvore

Chame useContext na raiz do seu componente para ler e se inscrever em contexto.

import { useContext } from 'react';

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

useContext retorna o valor do contexto para o contexto que você passou. Para determinar o valor do contexto, o React pesquisa na árvore de componentes e encontra o provedor de contexto mais próximo acima para esse contexto específico.

Para passar contexto para um Button, envolva-o ou um de seus componentes pai no provedor de contexto correspondente:

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

function Form() {
// ... renderiza botões dentro ...
}

Não importa quantas camadas de componentes existam entre o provedor e o Button. Quando um Button qualquer lugar dentro de Form chama useContext(ThemeContext), ele receberá "dark" como o valor.

Pitfall

useContext() sempre procura o provedor mais próximo acima do componente que o chama. Ele pesquisa para cima e não considera provedores no componente de onde você está chamando 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>
  );
}


Atualizando dados passados via contexto

Frequentemente, você vai querer que o contexto mude com o tempo. Para atualizar o contexto, combine-o com state. Declare uma variável de estado no componente pai e passe o estado atual como o valor do contexto para o provedor.

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Mudar para o tema claro
</Button>
</ThemeContext.Provider>
);
}

Agora qualquer Button dentro do provedor receberá o valor atual de theme. Se você chamar setTheme para atualizar o valor de theme que você passa para o provedor, todos os componentes Button serão re-renderizados com o novo valor 'light'.

Exemplos de atualização do contexto

Example 1 of 5:
Atualizando um valor via contexto

Neste exemplo, o componente MyApp mantém uma variável de estado que é então passada para o provedor ThemeContext. Marcar a caixa “Modo escuro” atualiza o estado. Alterar o valor fornecido re-renderiza todos os componentes que usam esse contexto.

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')
          }}
        />
        Usar modo escuro
      </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>
  );
}

Note que value="dark" passa a string "dark", mas value={theme} passa o valor da variável JavaScript theme com chaves JSX. As chaves também permitem que você passe valores de contexto que não são strings.


Especificando um valor padrão fallback

Se o React não encontrar nenhum provedor desse contexto na árvore pai, o valor do contexto retornado por useContext() será igual ao valor padrão que você especificou quando criou esse contexto (/reference/react/createContext):

const ThemeContext = createContext(null);

O valor padrão nunca muda. Se você quiser atualizar o contexto, use-o com estado como descrito acima.

Frequentemente, em vez de null, há algum valor mais significativo que você pode usar como padrão, por exemplo:

const ThemeContext = createContext('light');

Dessa forma, se você acidentalmente renderizar algum componente sem um provedor correspondente, não quebrará. Isso também ajuda seus componentes a funcionarem bem em um ambiente de teste sem precisar configurar muitos provedores nos testes.

No exemplo abaixo, o botão “Alternar tema” está sempre claro porque está fora de qualquer provedor de contexto de tema e o valor do tema de contexto padrão é 'light'. Tente editar o tema padrão para ser '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');
      }}>
        Alternar tema
      </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>
  );
}


Sobrescrevendo contexto para uma parte da árvore

Você pode sobrescrever o contexto para uma parte da árvore envolvendo essa parte em um provedor com um valor diferente.

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

Você pode aninhar e sobrescrever provedores quantas vezes precisar.

Exemplos de sobrescrita de contexto

Example 1 of 2:
Sobrescrevendo um tema

Aqui, o botão dentro do Footer recebe um valor de contexto diferente ("light") do que os botões fora ("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>Configurações</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>
  );
}


Otimizando re-renderizações ao passar objetos e funções

Você pode passar qualquer valor via contexto, incluindo objetos e funções.

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

Aqui, o valor do contexto é um objeto JavaScript com duas propriedades, uma das quais é uma função. Sempre que MyApp re-renderiza (por exemplo, em uma atualização de rota), isso será um objeto diferente apontando para uma função diferente, então o React também terá que re-renderizar todos os componentes profundamente na árvore que chamam useContext(AuthContext).

Em aplicações menores, isso não é um problema. No entanto, não há necessidade de re-renderizá-los se os dados subjacentes, como currentUser, não mudaram. Para ajudar o React a tirar proveito desse fato, você pode envolver a função login com useCallback e envolver a criação do objeto dentro de useMemo. Esta é uma otimização de desempenho:

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

Como resultado dessa alteração, mesmo que MyApp precise ser re-renderizado, os componentes que chamam useContext(AuthContext) não precisarão ser re-renderizados, a menos que currentUser tenha mudado.

Leia mais sobre useMemo e useCallback.


Solução de Problemas

Meu componente não vê o valor do meu provedor

Existem algumas maneiras comuns de isso acontecer:

  1. Você está renderizando <SomeContext.Provider> no mesmo componente (ou abaixo) de onde você está chamando useContext(). Mova <SomeContext.Provider> acima e fora do componente que chama useContext().
  2. Você pode ter esquecido de envolver seu componente com <SomeContext.Provider>, ou pode tê-lo colocado em uma parte diferente da árvore do que você pensou. Verifique se a hierarquia está correta usando React DevTools.
  3. Você pode estar enfrentando algum problema de build com suas ferramentas que faz com que SomeContext visto do componente provedor e SomeContext visto pelo componente leitor sejam dois objetos diferentes. Isso pode acontecer se você usar symlinks, por exemplo. Você pode verificar isso atribuindo-os a globais como window.SomeContext1 e window.SomeContext2 e depois verificando se window.SomeContext1 === window.SomeContext2 no console. Se eles não forem os mesmos, conserte esse problema no nível da ferramenta de build.

Estou sempre recebendo undefined do meu contexto, embora o valor padrão seja diferente

Você pode ter um provedor sem um value na árvore:

// 🚩 Não funciona: sem a prop value
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>

Se você esquecer de especificar value, é como passar value={undefined}.

Você pode também ter usado acidentalmente um nome de prop diferente por engano:

// 🚩 Não funciona: a prop deve ser chamada "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>

Em ambos os casos, você deve ver um aviso do React no console. Para corrigir, chame a prop de value:

// ✅ Passando a prop value
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>

Note que o valor padrão da sua chamada createContext(defaultValue) é usado se não houver um provedor correspondente acima de tudo. Se houver um <SomeContext.Provider value={undefined}> em algum lugar na árvore pai, o componente chamando useContext(SomeContext) receberá undefined como o valor do contexto.