ContextAPI - Como criar, como usar e como alimentar para iniciantes
Published: 2023-09-19 20:40:19

Neste artigo abordaremos um assunto bastante interessante no React chamado context, que disponibiliza uma maneira de passar os dados entre a árvore de componentes sem precisar passar props manualmente em cada nível.

Imagine o seguinte cenário, você tem o nome de um usuário em um componente, só pra gente ter uma noção entre camadas, vamos
chamá-los de A, B, C e D. Imagine que esse componente fez uma chamada de API no C, certo? Nós temos apenas o nome do usuário lá e
não queremos ter que ficar repetindo essa chamada nos outros componentes, certo?

## Ah, mas por que não? Ou por que não criamos no topo e vamos chamando até embaixo?
Vamos ao primeiro cenário. Imagine que ao invés de você fazer uma requisição pela página, você faz quatro para apenas uma página,
certo? Isso é tranquilo quando se trata de um usuário, mas já pensou se a nossa aplicação tem mil acessos diários? Isso seria
basicamente quatro mil requisições diária e isso é muita coisa para o banco de dados, isso sem contar as atualizações de página.

E por que não criamos a aplicação no topo e vamos chamando nas demais? Por um motivo quase que semelhante. Não tem necessidade de
você chamar nas camadas A, B e D, se o usuário sequer passou por lá, imagine que você só entrou na primeira página e saiu, certo?
E quando você vai ver, sem querer, você fez uma requisição na sub página que se quer o usuário entrou, é bem desgastante isso, não
concorda? Ainda mais para quem vive de internet móveis.

## Voltando ao Foco

Para facilitar essa comunicação, seja de A para D e D para B, por exemplo, existe o Context API do React e aí que entramos no
assunto. useContext, como funciona, de onde ele vem e como ele se alimenta. Em resumo, com o Context API podemos transitar esses
dados através de componentes sem que ele sejam filhos diretos desse componente. 

E vamos com calma entendendo passo a passo dessa situação, porque até para mim foi muito complexo entender no começo.

## Primeiro Passo

Primeiro, vamos importar e executar o método createContext

```jsx
import { createContext } from 'react'

const MyContext = createContext(null)
```

Inicialmente definimos ele como null, já que queremos ele vazio e não queremos definir um tipo para ele ainda.

DICA: O retorno do createContext é um componente, por isso é legal salvar numa variável em PascalCase, as famosas `{  }`

## Segundo Passo

Já que criamos ele vazio, como iremos adicionar dados a esse novo contexto que criamos?

Dentro do Provider definimos que esse contexto fornecerá pros componentes descendentes, deixando isso na propriedade value.

```jsx
const MyContext = createContext(null)

export default function Parent() {
  const nameUser = {
    name: 'Yagasaki',
  }

  // Outro exemplo
  const nameUser = ['Yagasaki']

  // Mais um exemplo
  const nameUser = 'Yagasaki'

  return (
    <MyContext.Provider value={nameUser}>
      <ComponentA />
    <MyContext.Provider>
  )
}
```

Está permitido colocar qualquer tipo de dado dentro contexto, já que ele aceita qualquer tipo, isso inclui até mesmo funções.

## Terceiro Passo

Agora que sabemos como criar um contexto, como adicionar dados a ele, como faremos para usar esse contexto como a gente bem entender?

Infelizmente não iremos usar esses dados fora do famoso pai-filho, ou seja, não adianta um componente "dinossauro" tentar acessar
informações do componente "peixe" já que eles não possuem nenhum parentes, é diferente do famoso "dinossauro" para "galinha", deu
para entender a analogia? Componentes que estão fora da árvore deles não possuem acesso, simples.

Mas como vamos acessar esses dados?

Primeiramente precisamos importa uma ou duas coisas, sendo: O Hook `useContext` do React e o Hook que a gente criou em outro
arquivo. Abaixo está um exemplo de como iremos usar o useContext a nosso favor.

```jsx
import { useContext } from 'react'

import MyContext from './MyContext.js'

export default function ComponentC() {
  const userContext = useContext(MyContext)

  return (
    <div>
      <p>{userContext.name}</p>
    </div>
  )
}
```

O seu retorno será exatamente a mesma informação que inserimos ali em cima na propriedade `value`. Assim podemos obter o dado
diretamente do provider sem NENHUM componente intermediário! Economizando um esforço que seria praticamente enorme para transitar esse dado abaixo por todos os
componentes intermédios! Simplesmente DEMAIS!

DICA: Note que obtemos o valor do nome como uma propriedade de userContext, isso porque no exemplo, criamos como um objeto na propriedade value, e esse mesmo objeto foi retornado da execução do useContext. Se for outro tipo de dado, o retorno será correspondente!

## Modificando o valor

Beleza, já criamos o provider, que vem do inglês de provedor, aquele que provê algo, providencia. Conseguimos inserir algo nele e acessamos ele através de um componente distante, e agora? Agora vamos aprender a modificar esse valor, manipular essa informação. Lembra que eu mencionei acima que ele aceita QUALQUER tipo de dado, inclusive funções? Então, e se por exemplo a gente usasse um `useState` nele?

Você deve se perguntar porque iriamos querer fazer isso, certo? Imagine que você está numa página de edição de usuário e quer que os dados desse usuário já estejam preenchidos quando o usuário entrar na página, como que a gente faz?

A ideia é o seguinte, vamos isolar a variável que a gente está passando para a propriedade `value` para facilitar a leitura e deixar um pouco mais organizado, okay? Nada de muito novo que fizemos aqui, é claro que estamos levando em consideração que ele é um objeto. Iremos apenas adicionar o valor do state e seu dispatch na variável providerValue e assim, inserimos no provider.

```jsx
const MyContext = createContext(null)

export default function Parent() {
  const [name, setName] = useState('Yagasaki')
  const providerValue = {
    name,
    setName,
  }

  return (
    <MyContext.Provider value={providerValue}>
      <ComponentA />
    </MyContext.Provider>
  )
}
```

Ficou confuso? O que fizemos a mais foi apenas adicionar o nome das duas variáveis que usamos no `useState` para assim que chamarmos de volta podermos manipular ele também dentro do componente, caso ao contrário, só precisariamos enviar o `name` e ele ficaria somente leitura, tendo que ser alterado apenas na página `Parent`

Dessa forma podemos alterar o valor da variável `name` a partir de qualquer componente que quisermos, como eu mencionei anteriormente, isso acaba sendo bem útil quando precisamos atualizar o valor de uma variável em ambos os lados, bacana né?

Olha como ficou o componente filho quando utilizamos o `useState` para passar a informação:

```jsx
import { useContext } from 'react'
import MyContext from './MyContext.js'

const ComponentA = () => {
  const userContext = useContext(MyContext)

  function handleChange(event) {
    const { newName } = event.target.value;

    if (true) {
      // Alguma verificação, se necessário
    }

    userContext.setName(newName)
  }

  return (
    <div>
      <label>
        Usuário: {userContext.name}
        <input type="text" value={userContext.name} onChange={handleChange}/>
      </label>
    </div>
  )
}

export default ComponentA
```

Só tome cuidado com loops infinitos, em casos de formulário, use o `form onSubmit={(e) => e.preventDefault()}`, assim, evitará de atualizar a página quando 
o usuário clicar em enviar, por exemplo, e claro, caso esteja usando um form.

Fico muito agradecido que você tenha chegado até aqui, levou um pouco de tempo para construir esse artigo, já que tentei deixar o mais direto e bem explicado possível para você entender de fato como funciona o Contexto, já que eu levei muito tempo para realmente entender como ele funciona.

Feedback no meu Twitter/X (@Yagasaki7K) são sempre bem vindos e espero que você que se dedicou a ler e entender esse artigo tenha aprendido algo novo.

Muito obrigado!

Você pode ver esse artigo escrito [originalmente no meu blog](https://yagasaki.dev/blog).