React: Contexto e estado global

Última atualização: 2026-10-26

1. O que você vai aprender



2. Uma história sobre a troca de temas

(1) Desafio: Passagem de props por 5 níveis de componentes

Alice quer adicionar um recurso de ativação/desativação do “modo escuro” ao aplicativo. O fluxo de dados deve passar por cinco camadas de componentes:

▶ Exemplo: Como usar o contexto

Saída:

TEXT
Estado baseado em Context compartilhado entre os componentes
▶ Experimente

Saída:

TEXT
Componente App com estado: 'light'. Renderiza uma interface interativa.

Pergunta da Alice:

(2) Solução para “Contexto”

▶ Experimente

Benefícios: Os componentes intermediários (Page, MainLayout, Sidebar) não precisam mais passar os props theme; os dados são passados diretamente do Provider para o Consumer.



3. Conceitos fundamentais do contexto

API Função Parâmetros/Valores de retorno
createContext(defaultValue) Criar um contêiner de contexto Voltar para { Provider, Consumer }
<Context.Provider value={...}> Fornece dados para a subárvore value: Qualquer tipo; aciona uma nova renderização do Consumer quando sofre alterações
useContext(Context) Recuperar os dados mais recentes do Provedor Retornar o value do Provedor; se nenhum Provedor for encontrado, retornar o valor padrão
Hook personalizado useXxx() Envolve o useContext + verificação de erros Padrão recomendado: lança um erro claro quando o Provider está ausente

(1) createContext + Provider + useContext

100%
graph TB
    A[createContext<br/>Create a context container] --> B[Provider<br/>Wrapper Component tree<br/>Provide value]
    B --> C[Middle Component<br/>Don't care Context]
    C --> D[useContext<br/>Consumption Context Data]
    
    style A fill:#1890ff,color:#fff
    style B fill:#52c41a,color:#fff
    style D fill:#ff6b6b,color:#fff
▶ Experimente

(2) Cenários em que o contexto é aplicável

Adequado para o contexto Não adequado para o contexto
Tema (Modo Escuro) Parâmetros de configuração dos componentes (usando Props)
Informações de login do usuário Valores inseridos no formulário (usando estado local)
Idioma/Internacionalização Dados da API (usando o TanStack Query)
Estado da rota Estado da animação (gerenciado pela biblioteca de animação)
Configurações globais Passagem simples de propriedades de pai para filho (usando propriedades diretamente)

Regra: Use Context apenas quando for necessário compartilhar dados entre “vários níveis e vários componentes”. Se você estiver simplesmente passando valores entre componentes pai e filho, os Props são suficientes.



4. Aninhamento de contextos e modularização de provedores

(1) Aninhamento de contextos em vários níveis

Os projetos do mundo real geralmente apresentam vários contextos:

JSX
function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <I18nProvider>
          <NotificationProvider>
            <MainApp />
          </NotificationProvider>
        </I18nProvider>
      </ThemeProvider>
    </AuthProvider>
  )
}
▶ Experimente

Para evitar o caos do aninhamento, você pode criar um componente composto AppProviders:

JSX
function AppProviders({ children }) {
  return (
    <AuthProvider>
      <ThemeProvider>
        <I18nProvider>
          <NotificationProvider>
            {children}
          </NotificationProvider>
        </I18nProvider>
      </ThemeProvider>
    </AuthProvider>
  )
}

function App() {
  return (
    <AppProviders>
      <MainApp />
    </AppProviders>
  )
}
▶ Experimente

(2) Hook de provedor personalizado

▶ Experimente

5. Otimização do desempenho em contexto

Quando o valor de um Contexto muda, todos os componentes que utilizam esse Contexto são renderizados novamente. Essa é a maior armadilha de desempenho dos Contextos.

Método de otimização Princípio Cenários aplicáveis
useMemo Valor estável Evite criar novas referências a objetos a cada renderização Quando o valor de um Provider contém uma função ou um objeto
Divisão por contexto Dados “mutáveis” e “imutáveis” são atribuídos a contextos diferentes Alguns consumidores precisam apenas de dados estáveis
React.memo Envolvendo o Consumer Ignorando a re-renderização quando os props não mudaram Quando o componente Consumer recebe outros props
Assinatura seletiva Use um seletor para recuperar apenas as fatias de que você precisa Implemente atualizações precisas com Zustand/Redux
JSX
// ❌ Question:Every time App Rendering,value They're all new partners. → All Consumer All are heavily rendered
function App() {
  const [count, setCount] = useState(0)
  return (
    <ThemeContext.Provider value={{ theme: 'dark', setCount }}>
      <ExpensiveTree />
    </ThemeContext.Provider>
  )
}

// ✅ Fix 1:use  useMemo Stable value Quote
function App() {
  const [count, setCount] = useState(0)
  const themeValue = useMemo(
    () => ({ theme: 'dark', toggleTheme: () => setCount(c => c + 1) }),
    []
  )
  return (
    <ThemeContext.Provider value={themeValue}>
      <ExpensiveTree />
    </ThemeContext.Provider>
  )
}

// ✅ Fix 2:separate "unchanged" and "changed" data into different Context
const SettingsContext = createContext({ theme: 'light', language: 'zh' })
const CountContext = createContext(0)

function App() {
  const [count, setCount] = useState(0)
  return (
    <SettingsContext.Provider value={settingsValue}> {/* useMemo Stable */}
      <CountContext.Provider value={count}>
        <ExpensiveTree />
      </CountContext.Provider>
    </SettingsContext.Provider>
  )
}
// Only when used CountContext The component will be count Re-render on changes
// SettingsContext Consumers will not be affected
▶ Experimente

6. Context + useReducer: Criando um estado global leve

Ao combinar a “propagação entre níveis” do Context com o “gerenciamento complexo de estado” do useReducer, é possível criar uma solução leve para o estado global:

▶ Expandir exemplo executável (125 linhas)
▶ Experimente

Saída:

TEXT
Exibe: "Button". Estado: theme (setter: setTheme). Botão: Button

Resultado esperado: Um sistema de notificações completo. Um ícone de notificação (exibindo o número de notificações não lidas) aparece na parte superior; no meio, os usuários podem enviar três tipos de notificações: informação, sucesso e erro; e na parte inferior, é exibida uma lista de notificações, na qual cada notificação pode ser marcada como lida ou excluída.


▶ Exemplo 2: Gerenciamento do status de autenticação do usuário (Contexto + Hook personalizado)

▶ Expandir exemplo executável (114 linhas)
▶ Experimente

Saída:

TEXT
Formulário de login com e-mail/senha. Formato de e-mail inválido → borda vermelha + "The email address format is incorrect.". Senha com menos de 6 caracteres → "Password must be at least 6 chars". Envio → console.log({email, password})

Saída:

TEXT
Subtítulo: "🔐 Log In". Exibe: "🔐 Log In". Estado: user (setter: setUser), loading (setter: setLoading), username (setter: setUsername). Botões: {loading ? 'Logging in......' : 'Log In'}, Log Out. Entrada: Username(Input admin Experience the Role of an Administrator), Password. Formulário com tratamento de envio. useMemo otimiza o cálculo. useCallback memoiza a referência do handler. Context fornece estado global. Busca assíncrona de dados / estados de carregamento. Comportamento baseado em temporizador

▶ Exemplo 3: Combinação de várias camadas de provedores com a internacionalização (i18n)

▶ Experimente

Saída:

TEXT
Internacionalização: o seletor de idioma altera todos os textos. Padrão Context + provider para suporte a vários idiomas

Saída:

TEXT
Subtítulo: "{t('greeting')}". Exibe: "Chinese". Estado: locale (setter: setLocale). Menu suspenso: Chinese, English. useMemo otimiza o cálculo. useCallback memoiza a referência do handler. Context fornece estado global

▶ Exemplo 4: Divisão do contexto para otimizar o desempenho

▶ Experimente

Saída:

TEXT
Componente SplitContextApp com estado: null, false. Renderiza uma interface interativa.

Saída:

TEXT
Exibe: "Login". Estado: user (setter: setUser), loading (setter: setLoading). Botão: login('Alice')}>Login. useMemo otimiza o cálculo. Context fornece estado global. Busca assíncrona de dados / estados de carregamento. Comportamento baseado em temporizador

▶ Exemplo 5: Exemplo completo do uso de useContext como alternativa ao treinamento com adjetivos

▶ Experimente

Saída:

TEXT
Componente SettingsApp com estado: {
    theme: 'light', fontSize: 14, sidebarCollapsed: false,
  }. Renderiza uma interface interativa.

❓ Perguntas Frequentes

P O que devo fazer se o Context do value for um objeto novo a cada vez, fazendo com que todos os Consumers sejam renderizados novamente?
R Use useMemo para estabilizar a referência do value: const value = useMemo(() => ({ theme, toggleTheme }), [theme]). Você também pode separar os dados “estáveis” e “em mudança” em diferentes Contexts. Se os problemas de desempenho persistirem após a otimização, considere usar o Zustand (que suporta atualizações precisas e não aciona a re-renderização de componentes não relacionados).
P O que devo fazer se houver muitas camadas de Contextos aninhados?
R ① Use um Provider para agrupar componentes (como o AppProviders mencionado anteriormente) e encapsular o aninhamento; ② Avalie se você realmente precisa de tantos Contextos — alguns dados “globais” são, na verdade, apenas “localmente globais” (como configurações para uma página específica) e não precisam ser colocados no Contexto de nível superior do aplicativo.
P Por que verificamos se context é nulo em um hook personalizado useXxx?
R Trata-se de uma técnica de programação defensiva. Se um desenvolvedor se esquecer de envolver um componente com um Provider antes de usar useContext, context será nulo (caso nenhum valor padrão tenha sido passado para createContext) ou causará erros difíceis de depurar. Exibir uma mensagem de erro clara logo no início é muito mais intuitivo do que fazer com que o usuário veja “Não é possível ler a propriedade de um valor nulo” no console do navegador.
P O Context pode substituir o Redux?
R Sim, em projetos de pequeno a médio porte, mas não em projetos grandes. O Context não possui três recursos que o Redux oferece: ① o mecanismo de middleware (registro em log, operações assíncronas, persistência); ② depuração com “viagem no tempo” (reprodução de alterações de estado no DevTools); ③ assinatura seletiva (uma alteração em um valor do Context faz com que todos os Consumers sejam renderizados novamente, enquanto o Redux permite usar seletores para assinar apenas as partes específicas de que você precisa). Se o seu projeto exigir esses três recursos, use o Zustand (leve) ou o Redux Toolkit.

📖 Resumo


📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Crie um hook LocaleProvider + useLocale que forneça os métodos locale (zh/en) e toggleLocale() para permitir a alternância entre chinês e inglês.
  2. Exercício avançado (Dificuldade ⭐⭐): Crie um hook AuthProvider + useAuth que forneça os métodos user, login(username, password) e logout() para gerenciar o status de autenticação do usuário.
  3. Desafio (Dificuldade: ⭐⭐⭐): Crie um hook CartProvider + useCart e use Context + useReducer para implementar um carrinho de compras com funções para adicionar, remover, alterar quantidades e esvaziar o carrinho. Use useMemo para otimizar o código e evitar re-renderizações desnecessárias.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%