Junior Alves
Senior Developer
Foto: Unsplash
26 de agosto de 2020 • 8 minutos de leitura
Criando um ToDo List para entender o React
Com esse exemplo básico, vamos aprender conceitos muito importantes do React.
Introdução
Existem vários conceitos que precisamos entender para dominar essa lib. Mas podemos dizer que existem três que são os mais importantes.
Esses conceitos estão presentes em praticamente todas aplicações construídas com React, podemos até ir além e dizer que são a base do React.
E que conceitos tão importantes são esses?
Componentes, estado e propriedades(props). Então bora entender esses conceitos na prática? Bora!
Vamos iniciar nosso projeto
No post sobre create-react-app (se não viu, está aqui), utilizamos o componente App.js que é criado por padrão.
Nesse projeto, vamos apagar ele e criar o nosso próprio componente, o Todo.js!
import React from 'react';
function Todo() {
return (
<div className="todo-wrapper">
<h1>ToDo List</h1>
<form>
<input type="text" placeholder="Adicione uma tarefa" />
<button type="submit">Adicionar</button>
</form>
<ul className="todo-list">
<li>Item</li>
</ul>
</div>
);
}
export default Todo;
Precisamos agora capturar o input do usuário, para isso, criamos uma função como abaixo:
function handleChangeInput(event) {
const inputTask = event.target.value;
}
E passar a handleInputTask no onChange do input:
<input
type="text"
placeholder="Adicione uma tarefa"
onChange={handleChangeInput}
/>
Blz, mas como fazemos para adicionar o novo item na lista?
Precisamos saber duas coisas primeiro:
- Não podemos alterar uma variável diretamente em React, precisamos utilizar estado (state);
- Precisamos criar um função que será executada assim que o usuário clicar no botão "Adicionar".
Primeiro vamos criar um estado para salvar o input do usuário:
import React, { useState } from 'react'; // <------ importar o useState do React
function Todo() {
const [task, setTask] = useState(''); // <------ estado
// ...
}
Nossa função handleChangeInput fica assim:
function handleChangeInput(event) {
const inputTask = event.target.value;
setTask(inputTask); // <----- atualizamos o estado "task" atraves de "setTask"
}
O useState é um Hook do React, que nos permite trabalhar com estados. Retorna um valor (task) e uma função para atualizar o valor (setTask).
*Por convenção, criamos o nomeEstado e a função setNomeEstado*.
Próximo passo: criar uma lista onde será salvo nossos itens
Já estamos atualizando o valor do estado, mas ainda precisamos de algo a mais.
Precisamos entrar com um valor no input, clicar em "adicionar" e então, o item é adicionado na nossa lista.
Será necessário outro estado para nossa lista de tarefas:
const [itemsList, setItemsList] = useState([]); // <----- será um Array
Criando a função que adiciona o item na lista
Adicionando items na lista:
// Adiciona um novo elemento na lista
function handleAddItemToList(event) {
event.preventDefault(); // <----- desabilita o refresh na pagina ao enviar um formulário
setItemsList([...itemsList, task]); // <----- Copia todos os items ja existentes e entao adiociona o novo item
}
Basta passarmos essa função ao onSubmit
do formulário:
<form onSubmit={handleAddItemToList}>
Para começarmos a ver funcionando, precisamos alterar nossa ul
:
<ul className="todo-list">
{itemsList.map(
(
item,
index // <----- Percorremos o array de todos
) => (
<li key={index}>{item}</li> // <----- Para cada item do array, criamos um `li`
)
)}
</ul>
OBS.: No React, quando lidamos com Arrays, é muito importante passarmos uma
key
para o primeiro elemento que será renderizado, nesse caso é oli
. Note que utilizamos oindex
do array como key.
Agora, queremos que ao clicar no botão de adicionar, o campo de input fique limpo.
Podemos fazer assim:
function handleAddItemToList(event) {
event.preventDefault();
setItemsList([...itemsList, text]);
// Limpa o campo de input
setTask('');
}
Basta chamar o setTask e passar vazio para ele.
Mas para que funcione de fato, precisamos também passar o valor de "task" como "value" do input, assim:
<input
type="text"
placeholder="Adicione uma tarefa"
onChange={handleChangeInput}
value={task}
/>
Ok, tudo certo né?
Bom, ainda não, porque temos um efeito colateral, pois se não inserirmos nada no input e clicar em adicionar, será adicionado um item vazio, não é isso o que queremos.
Para corrigir, basta criarmos uma verificação antes de adicionarmos um novo item na lista, dessa forma:
function handleAddItemToList(event) {
event.preventDefault();
if (!task) {
// <----- Se nao tiver vazio, nao faz nada
return;
}
setItemsList([...itemsList, task]);
setTask(''); // <----- Reseta o valor do input
}
O código completo até aqui
import React, { useState } from 'react';
function Todo() {
const [task, setTask] = useState('');
const [itemsList, setItemsList] = useState([]);
function handleChangeInput(event) {
const inputTask = event.target.value;
setTask(inputTask);
}
function handleAddItemToList(event) {
event.preventDefault();
if (!task) {
return;
}
setItemsList([...itemsList, task]);
setTask('');
}
return (
<div className="todo-wrapper">
<h1>ToDo List</h1>
<form onSubmit={handleAddItemToList}>
<input
type="text"
placeholder="Adicione uma tarefa"
onChange={handleChangeInput}
value={task}
/>
<button type="submit">Adicionar</button>
</form>
<ul className="todo-list">
{itemsList.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default Todo;
Se preferir, esse é o commit: https://github.com/jjunior96/studies-react/commit/221795792609ceeb876380008fa9716ccc324e27
Separando os componentes
Um dos conceitos importantes do React é justamente a componentização. Podemos criar interfaces visuais complexas, que são compostas por vários componentes simples, componentes que são especialistas no que fazem.
Por exemplo:
Usando meu próprio site como exemplo, podemos ver que a interface pode ser divida em vários componentes. Assim, podemos reutilizar componentes entre diversas páginas dentro do mesmo projeto, ou até em projetos diferentes!
Bom, de volta ao código, vamos criar uma pasta components, dentro dela, outra pasta com o nome de List e um arquivo index.js
src/components/List/index.js
Recortamos nossa lista que esta dentro de Todo.js e criarmos nosso componente dentro de List/index.js, dessa forma:
// src/components/List/index.js
import React from 'react';
function List(props) {
return (
<ul className="todo-list">
{props.itemsList.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
export default List;
Agora, dentro de Todo.js, precisamos importar esse componente List. Para isso, basta fazermos:
// Importa meu componente List
import List from './components/List';
E chamar esse componente, passando as props, que no caso é o itemsList:
// src/Todo.js
// {...}
return (
<div className="todo-wrapper">
<h1>ToDo List</h1>
<form>
<input type="text" onChange={handleInputText} value={text} />
<button type="submit" onClick={addItem}>
Adiciona
</button>
</form>
<List itemsList={itemsList} />{' '}
{/* <--------- passando o `itemList` como props para o componente */}
</div>
);
// {...}
Continuando nossa refatoração
Vamos criar um componente de Input
e outro de Button
. São bem simples, pra gente treinar a componentização. :)
// src/components/Input/index.js
import React from 'react';
function Input(props) {
return (
<input
onChange={props.onChange}
placeholder={props.placeholder}
value={props.value}
type={props.type}
/>
);
}
export default Input;
// src/components/Button/index.js
import React from 'react';
function Button(props) {
return <button type={props.type}>{props.children}</button>;
}
export default Button;
DICA: Coloque um console.log() para ver o que vem nas
props
dos componentes, para entender o real funcionamento das propriedades dentro do React. Altere as props enviadas e veja o que acontece!
Indo além...
Agora vamos criar um novo componente para o form.
// src/components/Form/index.js
import React from 'react';
function Form(props) {
return <form onSubmit={props.onSubmit}>{props.children}</form>;
}
export default Form;
Agora, no componente Todo (componente pai), vai ficar assim:
import React, { useState } from 'react';
import Button from './components/Button';
import Form from './components/Form';
import Input from './components/Input';
import List from './components/List';
import './todo.css';
function Todo() {
const [task, setTask] = useState('');
const [itemsList, setItemsList] = useState([]);
function handleChangeInput(event) {
const inputTask = event.target.value;
setTask(inputTask);
}
function handleAddItemToList(event) {
event.preventDefault();
if (!task) {
return;
}
setItemsList([...itemsList, task]);
setTask('');
}
return (
<div className="todo-wrapper">
<h1>ToDo List</h1>
{/* Nosso componente de Form */}
<Form onSubmit={handleAddItemToList}>
{/* Nosso componente de Input */}
<Input
type="text"
placeholder="Adicione uma tarefa"
onChange={handleChangeInput}
value={task}
/>
{/* Nosso componente de Button */}
<Button type="submit">Adicionar</Button>
</Form>
{/* Nosso componente de List */}
<List itemsList={itemsList} />
</div>
);
}
export default Todo;
Nosso ToDo List já está adicionando novas tarefas, porém...
Como pode perceber, a aplicação está funcionando, porém ainda está bem "feia". Podemos melhorar isso, e vamos!
No próximo post, vamos adicionar alguns estilos CSS para nossa aplicação ficar show!
Além disso, precisamos também adicionar as funcionalidades de:
- Marcar tarefa como feita
- Excluir tarefa
Vamos criar tudo isso no próximo post! :D
Conclusão
Bom, chegamos ao fim de mais um estudo sobre React. Aprendemos muitos conceitos importantes, como componentes, estados e propriedades. Esses 3 conceitos são a base principal de qualquer aplicação construída utilizando React. Entender com bastante clareza esses 3 pontos é extremamente importante para evoluir.
Código desenvolvido nesse post
Espero que eu tenha ajudado você a entender um pouco melhor esses conceitos. Qualquer dúvida, deixe nos comentários, ficarei muito feliz em responder. :D
Curtiu? Compartilhe esse post: