React: Contexto e estado global
Última atualização: 2026-10-26
1. O que você vai aprender
- createContext e Provider
- useContext: contexto de consumo
- Exercícios de contexto versus exercícios de props
- Aninhamento em vários níveis e otimização de desempenho
- Combinando Context e useReducer (Gerenciamento leve de estado)
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:
Estado baseado em Context compartilhado entre os componentes
Saída:
Componente App com estado: 'light'. Renderiza uma interface interativa.
Pergunta da Alice:
themeÀ medida que os dados são transmitidos do aplicativo, passando por cada camada até o botão, as três camadas intermediárias, na verdade, não precisam desses dados- Se adicionarmos mais uma informação do usuário para ser transmitida, as propriedades ficarão extremamente pesadas.
- Este é o clássico treinamento com adereços
(2) Solução para “Contexto”
▶ ExperimenteBenefí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
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
(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:
function App() {
return (
<AuthProvider>
<ThemeProvider>
<I18nProvider>
<NotificationProvider>
<MainApp />
</NotificationProvider>
</I18nProvider>
</ThemeProvider>
</AuthProvider>
)
}
Para evitar o caos do aninhamento, você pode criar um componente composto AppProviders:
function AppProviders({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<I18nProvider>
<NotificationProvider>
{children}
</NotificationProvider>
</I18nProvider>
</ThemeProvider>
</AuthProvider>
)
}
function App() {
return (
<AppProviders>
<MainApp />
</AppProviders>
)
}
(2) Hook de provedor personalizado
▶ Experimente5. 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 |
// ❌ 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
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)
Saída:
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)
Saída:
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:
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)
▶ ExperimenteSaída:
Internacionalização: o seletor de idioma altera todos os textos. Padrão Context + provider para suporte a vários idiomas
Saída:
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
▶ ExperimenteSaída:
Componente SplitContextApp com estado: null, false. Renderiza uma interface interativa.
Saída:
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:
Componente SettingsApp com estado: {
theme: 'light', fontSize: 14, sidebarCollapsed: false,
}. Renderiza uma interface interativa.
❓ Perguntas Frequentes
Context do value for um objeto novo a cada vez, fazendo com que todos os Consumers sejam renderizados novamente?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).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.context é nulo em um hook personalizado useXxx?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.📖 Resumo
createContext→.Provider→useContextTrês etapas para usar o Context- O Context resolve o Props Drilling, mas não resolve o problema do “gerenciamento de estado”
- Armadilha de desempenho: Alterações no valor do contexto fazem com que todos os consumidores sejam renderizados novamente; otimize usando
useMemo - Personalizar o gancho
useXxx+ verificação de erros é uma prática recomendada Context + useReducerpode servir como uma solução leve para o estado global, mas, para cenários complexos, ainda é melhor usar o Zustand ou o Redux.
📝 Exercícios
- Problema básico (Dificuldade ⭐): Crie um hook
LocaleProvider+useLocaleque forneça os métodoslocale(zh/en) etoggleLocale()para permitir a alternância entre chinês e inglês. - Exercício avançado (Dificuldade ⭐⭐): Crie um hook
AuthProvider+useAuthque forneça os métodosuser,login(username, password)elogout()para gerenciar o status de autenticação do usuário. - Desafio (Dificuldade: ⭐⭐⭐): Crie um hook
CartProvider+useCarte use Context + useReducer para implementar um carrinho de compras com funções para adicionar, remover, alterar quantidades e esvaziar o carrinho. UseuseMemopara otimizar o código e evitar re-renderizações desnecessárias.