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
valuediferente. Os valores anteriores e próximos são comparados com a comparaçãoObject.is. Pular re-renderizações commemonã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
SomeContextque você usa para fornecer o contexto eSomeContextque 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
SomeContextvisto do componente provedor eSomeContextvisto 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.SomeContext1ewindow.SomeContext2e depois verificando sewindow.SomeContext1 === window.SomeContext2no 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.