Menu

Hook useContext no React: compartilhe dados sem props

useContext lê um valor que um componente pai fornece, para que um componente bem aninhado o receba sem que cada componente no meio o passe como prop. Aprenda createContext, providers, atualizar o context a partir de um filho e como o context afeta as re-renderizações.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

useContext lê um valor que um componente pai fornece, para que um componente no fundo da árvore o receba sem que cada componente no meio o passe como prop. Você cria um context com createContext, envolve parte da árvore em um provider com um value e chama useContext(TheContext) onde precisar desse valor.

Toolbar nunca menciona o tema, mas os dois botões mudam quando você marca a caixa. É esse o objetivo do context: os componentes do meio não carregam uma prop que não usam, que é o problema de prop drilling que o context resolve.

Os três passos

// 1. Create it once, outside any component, usually in its own file
export const ThemeContext = createContext('light');

// 2. Provide a value to a part of the tree
<ThemeContext value={theme}>
    <Page />
</ThemeContext>

// 3. Read it in any component inside that part
const theme = useContext(ThemeContext);

No React 19 o próprio objeto de context é o provider. Código mais antigo escreve <ThemeContext.Provider value={theme}>, que continua funcionando, então você vai ver os dois.

O useContext procura acima na árvore o provider mais próximo daquele context. Um componente lê o mais próximo, então você pode aninhar providers para sobrescrever um valor em uma seção.

O valor padrão

O argumento de createContext é o que o useContext retorna quando nenhum provider fica acima do componente. Ele não muda quando o estado muda; é uma alternativa de reserva.

O primeiro Greeting não tem provider acima e recebe o padrão 'en'. O segundo lê 'es', e o terceiro lê o 'ja', mais próximo. Troque o value="es" de fora por value="ja" e só a segunda linha muda.

Atualizando o context a partir de um filho

O context só passa um valor para baixo. Para deixar um filho profundo mudá-lo, guarde o valor no estado onde está o provider e coloque o setter no context ao lado dele.

LoginButton chama setUser, o estado em App muda, o provider recebe um novo valor, e Header também atualiza. Nenhum dos componentes recebeu uma prop.

Todo consumidor renderiza de novo quando o valor muda

Quando o value de um provider muda, o React renderiza todo componente que chama useContext para ele, mesmo que esse componente esteja dentro de um memo e as props dele não tenham mudado. O React compara o valor antigo e o novo com Object.is. Então value={{ user, setUser }} é um objeto novo a cada renderização de App, e todo consumidor renderiza de novo mesmo quando user não mudou.

Os dois leitores registram uma vez na primeira renderização. Clique no botão: só PlainReader registra de novo, porque o context dele recebeu um objeto novo. O useMemo entrega ao MemoContext o mesmo objeto até name mudar, então MemoReader fica parado. Isso só importa quando a renderização fica lenta; para um app pequeno, um objeto novo por renderização não é problema.

Dividindo contexts

Um valor que muda com frequência e um valor que nunca muda não devem compartilhar um context. Se o setter fica junto com o estado, um componente que só precisa chamar setUser ainda renderiza toda vez que user muda. Coloque-os em dois contexts e cada consumidor assina só o que lê:

const UserContext = createContext(null);
const SetUserContext = createContext(() => {});

function UserProvider({ children }) {
    const [user, setUser] = useState(null);
    return (
        <SetUserContext value={setUser}>
            <UserContext value={user}>{children}</UserContext>
        </SetUserContext>
    );
}

O setUser do useState mantém a mesma identidade para sempre, então componentes que leem só o SetUserContext nunca renderizam por causa de um login. A mesma ideia vale para dados sem relação: um tema e um carrinho de compras ficam em contexts separados, não em um grande AppContext.

Um toque final comum é um hook personalizado como useUser(), que chama useContext(UserContext) e lança um erro claro quando o provider não existe, para que quem chama nunca importe o objeto de context diretamente.

Context, props ou uma biblioteca de estado

  • Props são o padrão. Passar um valor dois ou três níveis para baixo é claro e fácil de rastrear.
  • Context combina com valores de que muitos componentes precisam em muitas profundidades e que mudam raramente: tema, idioma, o usuário logado, um objeto de feature flags.
  • Uma biblioteca de estado (Redux Toolkit, Zustand, Jotai e outras) combina com estado grande que atualiza com frequência, em que você quer que um componente assine uma fatia e pule as renderizações causadas pelo resto. O context não tem esse seletor: um consumidor renderiza a qualquer mudança no valor.

Antes de recorrer a context ou a uma biblioteca, veja se passar componentes como children elimina o drilling. Muitas vezes elimina, sem API nova nenhuma.

Perguntas frequentes

O que o useContext faz no React?

Ele retorna o valor atual de um context, tirado do provider mais próximo acima do componente na árvore. Quando o valor desse provider muda, todo componente que o lê renderiza de novo.

Para que serve o valor padrão em createContext?

É o que o useContext retorna quando não há provider acima do componente. Ele é útil para testes e para componentes renderizados sozinhos, e nunca muda.

Ainda preciso do Context.Provider no React 19?

Não. No React 19 você pode renderizar o próprio context como provider: <ThemeContext value="dark">. <ThemeContext.Provider value="dark"> continua funcionando, então código mais antigo segue rodando.

Como atualizo o valor de um context a partir de um componente filho?

Guarde o valor no estado do componente que renderiza o provider e coloque o setter no context ao lado do valor: value={{ theme, setTheme }}. O filho lê setTheme com useContext e o chama.

O context do React substitui o Redux?

O context passa um valor para baixo na árvore; ele não gerencia estado sozinho. Combinado com useState ou useReducer, ele atende muitos apps. Uma biblioteca de estado adiciona o que falta ao context, como assinar só uma fatia para que atualizações sem relação não renderizem um componente de novo, além de devtools e middleware.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR