Context
Elevar o estado funciona bem quando os componentes que compartilham um valor ficam próximos. Fica difícil quando não ficam. Um valor que fica perto do topo da árvore e é necessário seis níveis abaixo precisa ser passado por cada componente no meio, e cada um deles tem que aceitar e repassar uma prop que nunca usa. Esse repasse é prop drilling, o problema que Componentes compostos enfrentaram e não conseguiram resolver com Children.map. Context é a forma nativa do React de sair dessa: um componente fornece um valor, e qualquer componente abaixo dele pode ler esse valor diretamente, não importa quantas camadas estejam no meio.
Pense nisso como teleportar os dados. Os componentes no meio nunca veem isso e nunca repassam; suas listas de props continuam focadas no que eles fazem.
Criando um context e fornecendo um valor
Context tem três partes: criar, fornecer um valor e ler o valor. Criar acontece uma vez, no nível superior de um arquivo, fora de qualquer componente. Você geralmente também vai exportar, porque os componentes que leem isso costumam ficar em outros arquivos.
import { createContext } from 'react'
export const ThemeContext = createContext()
export default function App() {
return (
<ThemeContext value="light">
<Header />
<Button />
</ThemeContext>
)
}Renderizar o próprio objeto context como um componente faz com que tudo dentro dele seja um consumidor em espera. A prop value é a carga útil: aqui é a string "light", mas pode ser qualquer valor JavaScript, um objeto, um array, até uma função. O que você colocar lá é o que os leitores abaixo vão receber. A prop tem que ser escrita como value; esse nome faz parte da API.
O posicionamento importa. O provedor não precisa envolver todo seu app, e geralmente não deveria. Coloque-o ao redor da menor subárvore que cobre cada componente que precisa do valor. Se só um canto da UI se importa com o tema, forneça o tema no ancestral comum daquele canto e deixe o resto da árvore de fora.
Nota de versão
Antes do React 19 um context não podia ser renderizado diretamente; você renderizava <ThemeContext.Provider value="light"> em vez disso. React 19 tornou o próprio objeto context renderizável como o provedor. A forma .Provider ainda funciona e é o que você vai ver na maioria dos codebases existentes e nas aulas do curso, mas React planeja descontinuá-la em uma versão futura e fornece um codemod para fazer a mudança, então use a forma simples <ThemeContext> em código novo.
Lendo o valor com useContext
Qualquer componente abaixo do provedor lê o valor com useContext, o hook apresentado pela primeira vez em Hooks, passando para ele o objeto context para que React saiba qual context ler. É por isso que o context é exportado.
import { useContext } from 'react'
import { ThemeContext } from './App'
function Header() {
const theme = useContext(ThemeContext)
return (
<header className={`${theme}-theme`}>
<h1>{theme === 'light' ? 'Light' : 'Dark'} Theme</h1>
</header>
)
}useContext(ThemeContext) retorna o que o ThemeContext provedor mais próximo acima deste componente está mantendo atualmente: aqui, a string "light". Não há repasse de prop entre App e Header, e componentes entre eles em uma árvore maior nem mencionariam o tema. Um app pode ter vários contexts ao mesmo tempo, cada um criado separadamente e cada um lido passando seu próprio objeto para useContext.
Tornando vivo: state mais context
Um value="light" codificado nunca muda. O padrão que torna context útil o combina com state: state é dono do valor e o atualiza, context o entrega. Passe um objeto que contém tanto o valor atual quanto uma função que o muda, e todo consumidor pode ler ou atualizar o tema de qualquer lugar abaixo do provedor.
export const ThemeContext = createContext()
export default function App() {
const [theme, setTheme] = useState('light')
function toggleTheme() {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light')
}
return (
<ThemeContext value={{ theme, toggleTheme }}>
<Header />
<Button />
</ThemeContext>
)
}
function Button() {
const { theme, toggleTheme } = useContext(ThemeContext)
return (
<button className={`${theme}-theme`} onClick={toggleTheme}>
Switch theme
</button>
)
}Quando o botão é clicado, toggleTheme roda, o state em App atualiza, App renderiza de novo, e o provedor passa o novo objeto para baixo. Todo componente lendo o context renderiza de novo com o valor fresco. Consumidores desestrutura apenas as propriedades que precisam.
Esse truque de escopo também funciona em pequena escala. Um componente como um menu dropdown pode renderizar um provedor ao redor de seus próprios filhos, dando aos pedaços dentro dele um state compartilhado que nunca vaza para o resto da página. Aqui está o menu de Componentes compostos, terminado:
const MenuContext = createContext()
function Menu({ children }) {
const [open, setOpen] = useState(false)
const toggle = () => setOpen(prevOpen => !prevOpen)
return <MenuContext value={{ open, toggle }}>{children}</MenuContext>
}
function MenuButton({ children }) {
const { toggle } = useContext(MenuContext)
return <button onClick={toggle}>{children}</button>
}Menu é dono de open e coloca tanto isso quanto toggle no context; MenuButton chega lá procurando o toggle com useContext, e um MenuDropdown escrito do mesmo jeito lê open para decidir se renderiza. Nada no meio repassa nada, então um chamador pode envolver o botão em quantos divs de layout quiser e o menu continua funcionando.
Context deixa um componente perto do topo dizer "aqui está o valor" e deixa qualquer componente abaixo chegar até lá e pegá-lo diretamente com useContext. Os componentes no meio nunca tocam nele.
Combine com state e o valor pode mudar também: mantenha o valor e sua função de atualização juntos no provedor, e qualquer consumidor pode ler ou mudá-lo.
Próximo: Render props e componentes headless, onde um componente fornece comportamento e deixa você fornecer a marcação.

