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.
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.
Após o arquivo ter sido criado, devemos começar executando alguns passos para a criação do nosso contexto. Confira abaixo.
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.
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:
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
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
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:
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.
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.
Top comments (0)