React Hooks: aprenda a gerenciar estados com a Context API
Tags: react, javascript, hooks, frontend
Published: 2021-09-07 16:30:36

Se você está estudando React há algum tempo, muito provavelmente você já ouviu falar sobre a Context API. Esse hook é bastante usado para compartilhar dados entre componentes e serve para substituir o Redux em alguns casos.

Nesse artigo, vou explicar um pouco como a Context API funciona e porque ela é tão importante no ecossistema React.

## O que é Context API?

Context API é o hook responsável por compartilhar estados, funções e outros elementos entre os componentes. Usar esse hook permite que uma string, um número ou qualquer outro dado possa ser usado em qualquer outro componente contanto que ele seja armazenado corretamente dentro de um contexto.

No caso de um sistema de login, muito provavelmente você terá que passar o nome do usuário para diversos componentes. Para facilitar que o tudo isso fique organizado, você pode criar um contexto que armazenará essa informação, assim podendo ser compartilhada posteriormente entre vários componentes.

## Quais problemas a Context API resolve?

A Context API foi criado como uma forma de substituir problemas na passagem de dados, evitando que um estado tivesse que ser passado por vários componentes até chegar no elemento que realmente usará os dados.

Esse problema antigamente era solucionado usando o Redux, sendo a Context API uma forma de substituir essa biblioteca nesse caso especifico. Apesar disso, o Redux ainda é bastante usado, uma vez que a Context API segue um único fluxo de transmissão de dados, passando os conteúdos dos pais para os filhos quando há múltiplos contextos.

No caso do Redux, pode acontecer dos filhos passarem propriedades para os pais, uma vez que ele não depende do fluxo unidirecional na aplicação por causa da sua store.

## Criando um context

Os contexts (ou contextos) são responsáveis por armazenar os dados que serão compartilhados entre componentes. Idealmente, os contexts ficam armazenados numa pasta própria e devem ser chamados usando o hook useContext.

```jsx
// Calling someone context
const { SomeFunction } = useContext(someoneContext);
```

Antes de usar um desses contextos, precisamos defini-los. Para isso, utilizamos createContext. Esse método permite que a base do contexto seja criada, apesar de não haver dados sendo armazenados até esse momento.

```jsx
export const SomeContext = createContext()
```

## Criando um provider

Para começarmos a adicionar propriedades nesse contexto, precisamos criar uma função que servirá como um provedor. Normalmente, essa função é escrita com a palavra "Provider" no final, apesar disso não ser uma regra.

```jsx
function FirstContextProvider() {
	return ()
} 
```

Dentro dessa função, precisamos especificar que ela é um provider. Para isso, colocamos no return com o nome do context criado, seguido de um ponto final e da palavra Provider.

```jsx
function FirstContextProvider({ children }) {
	return (
		<firstContext.Provider value={
		}>
			{children}
		</firstContext.Provider>
	)
} 
```

Note que nesse componente o atributo value foi declarado. Esse atributo será responsável por armazenar as funções compartilhadas.

```jsx
function FirstContextProvider({ children }) {
	function handleButton() {}

	return (
		<firstContext.Provider value={
			handleButton,
		}>
			{children}
		</firstContext.Provider>
	)
} 
```

No caso acima, a palavra children se refere ao componente filho que receberá os dados do Provider. Isso permite que tudo que estiver dentro do context possa ser usado pelo resto da sua aplicação e te ajuda a entender como o fluxo de dados está funcionando.

## Adicionando um provider na aplicação

Para que seu contexto funcione, você precisa adicionar o provider na aplicação. No caso React com create-react-app (CRA), você precisa adicionar diretamente no arquivo "app.js".

Para isso, basta colocar o provider como um componente normal, fazendo com que o App fique dentro do provider. Desse modo, qualquer coisa que foi exportada no value do provider pode aproveita no componente App e, por consequência, em toda sua aplicação. 

```jsx
ReactDOM.render(
  <FirstContextProvider>
    <App />
  </FirstContextProvider>,
  document.getElementById('root'),
)
```

Caso você esteja usando o NextJS, a ideia é a mesma. O que muda, na prática, é a estrutura do próprio NextJS que, no lugar do ReactDOM.render, utiliza uma função normal e o elemento Component no lugar do App.

```jsx
function MyApp({ Component, pageProps }) {
  return (
    <FirstContextProvider>
      <Component {...pageProps} />
    </FirstContextProvider>
  )
}

export default MyApp
```

Vale lembrar que contexts não são validos no server-side do NextJS, assim como hooks e outras ferramentas específicas do React. Para isso, você precisa pegar as informações já na página, não sendo possível pegar os dados do provider com getStaticProps, getInitialProps ou getServerSideProps. O mesmo se aplica para rotas dinâmicas usando getStaticPaths.

## Criando um hook

Caso prefira, você pode criar um hook para representar seu context. Lembrando que o context e o provider, apesar de estarem ligados, são coisas diferentes, e o que deve ser declarado no hook é o context.

No código, para criar um hook do context, você pode fazer nesse formato:

```jsx
export const useSomeContext = () => {
    return useContext(SomeContext);
}
```

O useSomeContext pode ser receber qualquer outro nome. No React, a palavra "use" é normalmente usada para descrever um hook, como useState ou useEffect, por exemplo.

## E no TypeScript?

Caso você queira usar TypeScript, você precisa definir duas coisas: os elementos que serão exportados no context e os elementos que estão entrando no provider.

No primeiro caso, os elementos que serão exportando no context poderão ser funções, estados ou qualquer outro item que possa ser reutilizado, como já explicado. Cada um deles precisa ser devidamente tipado e estará dentro de um type. Esse type será repassado quando o context for criado.

Para aplicarmos esse type no createContext, você precisa criar um objeto vazio e ligar a tipagem a ele. Veja o exemplo:

```jsx
type SomeoneContextData = {
	handleButton: () => void
}

export const SomeoneContext = createContext({} as SomeoneContextData)
```

No segundo caso, já no provider, é bem mais simples: você precisa definir qual o type da única propriedade que estão chegando no provider: o children. Esse children sempre será um componente do React, por isso, ele receberá o tipo ReactNode, que é importado diretamente da biblioteca do React.

```tsx
type FirstContextProviderProps = {
	children: ReactNode
}

function FirstContextProvider({ children }: FirstContextProviderProps) {
	function handleButton() {}

	return (
		<firstContext.Provider value={
			handleButton
		}>
			{children}
		</firstContext.Provider>
	)
} 
```

## Conclusão

Se você chegou até aqui, espero que tenha entendido qual problema a Context API resolve e como ela pode ser útil em seus projetos.

Caso ainda tenha alguma dúvida sobre a Context API ou queira dar algum feedback sobre esse conteúdo, por favor, deixe um comentário nessa postagem.