Gerenciamento de estado com useContext()
Published: 2021-11-06 03:24:05

Lidar com estados no React é uma tarefa complicada quando começamos a utilizar estados de componentes que são consumidos globalmente. Criar estados em um componente e passar seu valor de estado para filhos e netos pode tornar o gerenciamento um tanto quanto complexo até mesmo para aplicações menores. Aqui começamos a falar de conceitos mais específicos do React com o intuito de facilitar nosso processo de produção de aplicações. Dessa vez, falaremos do useContext(), ferramenta e conceito imprescindível para produção de aplicações escaláveis e de manutenção eficiente. Vamos.

## Início de projeto

Iremos criar um simples ToDo que receberá atividades a serem executadas. Definiremos um contexto global e utilizaremos este conceito para que a aplicação funcione corretamente.

Abaixo você verá os meus diretórios da aplicação e o conteúdo de cada um. Neste artigo me limitarei a falar apenas acerca do conceito do estado, o conhecimento básico acerca da criação dos componentes subtende-se que o leitor já deva possuir.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avp2vbdegr5vet23f8qu.PNG)

Os componentes estão todos em diretórios individuais dentro do diretório components. Desse modo organizamos o nosso código de uma maneira mais eficiente.

## Lidando com contexto

Para que possamos entender o context precisamos inicialmente conhecer o conceito do state.

Quando estamos produzindo uma aplicação, é inevitável que em algum momento comecemos a reutilizar constantes e estados, isso se dá pelo dinamismo de informações que estão dispostas no processo produtivo do desenvolvimento. Geralmente, lidar com uso de dados de forma global, principalmente no React, costuma ser confuso, já que quando lidamos com a utilização de vários elementos, o dado pode ficar confuso em meio a tantos parents da cadeia de componentes. Imagine só, definir um state em um elemento pai e sair distribuindo este valor para filhos e mais filhos na aplicação. Em algum momento a confusão deve tomar de conta de tudo. Para isso, criamos contextos, que são estados(states) definidos globalmente que podem ser utilizados em qualquer parte da nossa aplicação. Isso é bem mais prático do ponto de vista produtivo, já que só precisaremos lidar com o estado definido num arquivo separado. Sabendo disso, vamos começar a aplicar o useContext, ferramenta fundamental para entendermos o gerenciamento de estado em nossas aplicações.


## Criando um contexto

Inicialmente iremos criar o contexto onde nossas constantes globais serão armazenadas. É daqui que iremos exportar o estado para utilizarmos em qualquer lugar que quisermos da nossa aplicação.

Para isso, criaremos um novo diretório chamado de ``context``. Dentro dele, armazenaremos um novo arquivo com o formato ``.jsx`` de nome ContextTodo. Esse aquivo armazenará os estados que precisamos. Ficamos então com esta estrutura.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/enkvqga36yp4a7lc7290.PNG)

Após o arquivo ter sido criado, devemos começar executando alguns passos para a criação do nosso contexto. Confira abaixo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gt0cfgccyorl2c1cgkr.PNG)

Como de praxe, começamos importando o react. E como nova ferramenta, utilizaremos o hook ``useContext`` que importaremos também do react. Além de claro, nosso bom e velho ``useState``.

Após importados, definiremos uma constante chamada de ``ContextTodo`` (elemento que armazena o nosso estado global) e criaremos um contexto na mesma através do hook react ``createContext()``. 

Para gerenciarmos e definirmos o que acontece dentro deste contexto criado utilizaremos o Provider. O provider neste caso será uma constante que armazena uma arrow function, esta constante(TodoProvider) fará uso do nosso contexto. Sendo assim, a aplicação ficará toda contida dentro do nosso provider.

A utilização do ``{children}`` sendo utilizado como uma prop tem como intuito desconstruir os elementos que serão definidos ao longo da aplicação.


## Definindo o provider

Após criada nossa constante TodoProvider, devemos começar a criar alguns métodos de gerenciamento de estado. Abaixo você pode ver a estrutura criada e a explicação de cada elemento.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhirsmht6jgx52ppuhbq.PNG)

iniciamos uma constante ``todos`` que armazenará um estado. Este estado é um array de objetos que armazenará simples tarefas de uma lista de afazeres. Cada elemento do array guarda um id, um title e um status de conclusão. sendo assim:

```
const [todos, setTodos] = useState([
        {id: 1, title: 'Ir ao supermercado', done: false},
        {id: 2, title: 'Ir para academia', done: false},
        {id: 3, title: 'Passear com o cachorro', done: false},
    ]);

```

Após definido o estado principal do ToDo, criaremos uma nova constante ``saveTodo`` que salvará uma nova tarefa através de sua função. Esta função recebe o ``todo`` como parâmetro e cria uma nova constante ``newTodo`` que armazena desta vez uma nova tarefa dentro de um objeto, que posteriormente será armazenado dentro do nosso array de objetos definidos.

Além disso, a função ``saveTodo`` armazenará dentro do nosso estado ``todos`` o novo valor obtido através do ``newTodo``.

Sendo assim:

```
const saveTodo = todo => {
        const newTodo = {
            id: todos.length + 1,
            title: todo.title,
            done: false,
        };
        setTodos([...todos, newTodo]);
    };

```

Além disso, nosso TodoProvider retorna um novo elemento, o ``<ContextTodo.Provider></ContextTodo.Provider>`` A utilização do elemento ``.Provider`` não é um equívoco, trata-se de um componente que está contido no objeto Context. Ele permite que componentes consumidores a assinarem mudanças no contexto, ou seja, gerenciar o contexto definido. 

Segundo a própria documentção do React: O ``.Provider`` contém uma prop ``value``, ela pode ser passada para ser consumida por componentes que são descendentes deste Provider. Um Provider pode ser conectado a vários consumidores. Providers podem ser aninhados para substituir valores mais ao fundo da árvore. Todos consumidores que são descendentes de um Provider serão renderizados novamente sempre que a prop ``value`` do Provider for alterada.


Desse modo, passamos dentro do nosso provider dois valores: O primeiro trata-se da lista de tarefas definida na constante ``todos``, e o segundo é a nossa nova tarefa ``savetodo``.

Sendo assim, temos a seguinte estrutura:

```
return(
        <ContextTodo.Provider value={{todos, saveTodo}}>
            {children}
        </ContextTodo.Provider>
    );

```

Como os filhos do nosso provider conseguem utilizar os valores que o provider armazena, iremos definir o children como sendo o nosso app. Sendo assim, utilizamos as chaves passando o children dentro do nosso ``<ContextTodo.Provider></ContextTodo.Provider>``.

## Utilizando nosso estado global no App

Depois de definido o Provider, precisamos executar este elemento dentro do nosso app, sendo assim, ele será o pai de toda a nossa aplicação neste caso em específico. A estrutura do nosso ``App.jsx`` fica da seguinte forma:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwlde3iypf2hn8rzm1ng.PNG)

Como podemos observar, o nosso ``TodoProvider`` é o elemento principal e dentro dele contém nossos componentes criados posteriormente ``ToDoList`` e ``AddTodo``.

#### Entendendo os componentes criados



#####ToDoListItem

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akv9miv1e0jofljo7v51.PNG)

O componente contém uma prop chamada ``todo``, ele retorna uma div que contém o id, o título e o status da tarefa de cada um das tarefas passadas.


#####ToDoList

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54cj74w8f6jg49l9xykc.PNG)

Analisando agora o componente ``ToDoList`` podemos ver a utilização do componente ``ToDoListItem`` descrito posteriormente.

Para que possamos utilizar o contexto global definido na constante ContextTodo, devemos criar uma nova e defini-la como sendo o elemento que irá receber o uso de um contexto. Neste caso, utilizamos a constante ``context`` para usar o contexto da ``ContextTodo``. Conforme abaixo:

```
const context = useContext(ContextTodo);
```
Após definido, utilizaremos a constante ``data`` para receber e renderizar posteriormente cada um dos elementos do todo. Sendo assim, criamos um novo método ``.map`` para criar cada um dos elementos da lista de tarefas através do componente descrito anteriormente ``ToDoListItem``.

O método ``.map`` neste caso executa a seguinte lógica:

1 - Chama o componente ``<ToDoListItem>``;
2 - Define uma key como sendo o id de cada uma das tarefas;
3 - cria um novo parâmetro chamado ``todo`` que recebe o objeto ativo no ``.map``.

```
 const data = context.todos.map(todo=>(
        <ToDoListItem key={todo.id} todo={todo}></ToDoListItem>
    ));

```

O ToDolist retorna após isso o elemento data que agora guarda um array de tarefas.

Após isso, podemos ver nossas tarefas sendo renderizadas na tela conforme mostra a seguir:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r8k0h4nyoevm4iw5i6jk.PNG)

## Adicionando novas tarefas

A aplicação está quase pronta, para finalizarmos precisamos apenas definir o local onde iremos submeter novos dados para a nossa lista de tarefas. E como submetemos coisas? Através de formulários! Sendo assim, editaremos o nosso componente ``AddTodo``.

Abaixo você pode visualizar a estrutura do componente.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh6o11hz4fnr7uaqqp1w.PNG)

No nosso componente ``AddTodo`` começamos declarando a constante ``saveTodo`` que receberá o valor da constante ``saveTodo`` que está em nosso contexto global. 

```
const {saveTodo} = useContext(ContextTodo);
```

Logo após podemos declarar constantes com estados locais.

```
const [todo, setTodo] = useState();
```

Definimos agora a nova função dentro de uma constante chamada ``handleFormSubmit``. Essa função será responsável por adicionar o valor digitado num input na nossa lista de tarefas.


```
const handleFormSubmit = e => {
        e.preventDefault();
        saveTodo(todo);
    };
```

o comando ``saveTodo(todo)`` executa a função presente em nosso contexto tendo como parâmetro nossa constante ``todo`` já declarada.

Logo após definimos uma função que armazenará o valor digitado no input dentro da nossa cadeia de elementos ``todo`` através da desestruturação.

```
const handleInputChange = e => {
        setTodo({
            ...todo,
            title: e.target.value,
        });
    };
```

e para finalizar, retornamos o nosso formulário com o input e o botão para que as funções definidas sejam executadas quando as mudanças previstas acontecerem. No caso do input o ``onChange`` chama a função ``handleInputChange`` e o submit do formulário executa a função ``handleFormSubmit``.


```
return(
        <form onSubmit={handleFormSubmit}>
            <input type="text" name="title" id="title" placeholder="Nova Tarefa..." onChange={handleInputChange}/>
            <button>ADICIONAR</button>
        </form>
    );
```

Como podemos perceber, a utilização do useContext é muito importante para gerenciamento de dados dentro de aplicações react, pois facilita a cadeia organizacional dos elementos e distribui os dados de forma mais consistente. Existem outras alternativas para gerenciamento de estado, como utilizar a lib redux por exemplo, mas utilizar ferramentas da própria tecnologia é muito importante para entendermos os conceitos iniciais de funcionamento.

Quaisquer dúvidas sobre a temática sugiro consultar a própria documentação do React acerca desta temática.

A produção deste artigo tem primordialmente caráter didático, tendo sido totalmente inspirado no seguinte [vídeo](https://www.youtube.com/watch?v=D_yxtCD_Vi0&ab_channel=balta.io).