useContext
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);
// ...
Parâmetros
SomeContext
: O contexto que você criou anteriormente comcreateContext
. 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 chamadauseContext()
. - 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çãoObject.is
. Pular re-renderizações commemo
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 eSomeContext
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.
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'
.
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.
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:
- Você está renderizando
<SomeContext.Provider>
no mesmo componente (ou abaixo) de onde você está chamandouseContext()
. Mova<SomeContext.Provider>
acima e fora do componente que chamauseContext()
. - 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. - Você pode estar enfrentando algum problema de build com suas ferramentas que faz com que
SomeContext
visto do componente provedor eSomeContext
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 comowindow.SomeContext1
ewindow.SomeContext2
e depois verificando sewindow.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.