Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA

Aula 6 de 6

Projeto React: painel de tarefas completo

Reúna componentes, props, estado, formulário, listas e filtros em um painel de tarefas com fluxo acessível e critérios objetivos de conclusão.

75 minutos · leitura + prática · nível iniciante

Ao terminar esta aula, você vai conseguir

  • Definir onde cada estado deve morar
  • Compor formulário, filtros, resumo e lista
  • Verificar criação, conclusão, remoção e estados vazios
O componente App se divide nos componentes Header, Lista e Botão.

O projeto final é um painel de tarefas que adiciona, conclui, remove e filtra itens. O desafio principal não é escrever mais JSX; é manter uma fonte de verdade e um fluxo que você consegue explicar. App coordena o estado, enquanto componentes menores recebem somente os dados e callbacks necessários.

Pense numa central com um quadro único de pedidos. Formulário, contador e lista consultam o mesmo quadro, então não divergem. A comparação termina quando chega à implementação: React trabalha com snapshots imutáveis e fluxo de dados do pai para os filhos, não com pessoas editando simultaneamente um quadro físico.

Desenhe a árvore e localize o estado

Antes do código, separe as responsabilidades:

text
App
├── FormularioTarefa
├── Filtros
├── Resumo
└── ListaTarefas
    └── ItemTarefa

tarefas é usada pela lista e pelo resumo; filtro altera a lista; o formulário adiciona ao conjunto. O ancestral comum é App, então o estado mora nele. Os filhos recebem props. Isso é elevar o estado: mover a fonte de verdade para o nível que consegue coordenar todos os consumidores.

jsx
function App() {
  const [tarefas, setTarefas] = React.useState([]);
  const [filtro, setFiltro] = React.useState('todas');

  const visiveis = filtrarTarefas(tarefas, filtro);

  return (
    <main>
      <FormularioTarefa aoAdicionar={adicionar} />
      <Filtros atual={filtro} aoMudar={setFiltro} />
      <Resumo tarefas={tarefas} />
      <ListaTarefas tarefas={visiveis} aoAlternar={alternar} aoRemover={remover} />
    </main>
  );
}

visiveis é derivado e não precisa de outro estado. O número de pendentes também é calculado de tarefas. Menos duplicação significa menos combinações inválidas.

Escreva operações imutáveis

Cada ação devolve um novo array. Adicionar usa spread, alternar usa map e remover usa filter:

jsx
function adicionar(titulo) {
  setTarefas((atuais) => [
    ...atuais,
    { id: crypto.randomUUID(), titulo, concluida: false },
  ]);
}

function alternar(id) {
  setTarefas((atuais) => atuais.map((tarefa) =>
    tarefa.id === id ? { ...tarefa, concluida: !tarefa.concluida } : tarefa
  ));
}

function remover(id) {
  setTarefas((atuais) => atuais.filter((tarefa) => tarefa.id !== id));
}

Essas funções recebem eventos do usuário indiretamente pelos callbacks dos filhos. O id estável conecta a ação à tarefa e também serve como key na lista.

Trate o estado vazio como parte do produto

Uma lista vazia sem explicação parece falha. Se não existem tarefas, diga “Você ainda não criou tarefas”. Se existem tarefas, mas nenhuma corresponde ao filtro, diga “Nenhuma tarefa neste filtro” e ofereça um caminho para mostrar todas. A mensagem deve refletir a causa real.

Use botões reais para ações, label nos campos e texto compreensível além de ícones. Um resumo com aria-live="polite" pode anunciar a quantidade de pendentes. Verifique tudo pelo teclado: Tab alcança campo, filtro e botões; Enter envia o formulário; o foco visível não desaparece.

Persistência não entra no estado apenas por existir. Se você adicionar localStorage depois, leia uma vez na inicialização e grave quando tarefas mudarem, tratando JSON inválido. Se integrar uma API, modele também carregamento e erro. Nesta entrega, manter os dados em memória deixa o foco nos contratos entre componentes e torna o reinício previsível.

Teste como uma sequência de comportamentos

Execute estes oito testes manuais:

text
1. Enviar título vazio mostra orientação e não cria item.
2. Um título válido aparece uma vez e limpa o campo.
3. Concluir muda o item e reduz o total de pendentes.
4. Reabrir restaura o item e o total.
5. O filtro pendentes esconde somente os concluídos.
6. O filtro concluídas mostra somente os concluídos.
7. Remover apaga apenas o item escolhido.
8. Todas as ações funcionam pelo teclado e o vazio tem mensagem.

Depois rode o build do projeto Vite:

bash
npm run build

O build bem-sucedido prova que módulos e JSX foram processados; não prova os oito comportamentos, por isso as duas verificações se complementam.

Erro comum: duplicar a mesma verdade

Guardar tarefas, tarefasFiltradas e quantidadePendentes como estados independentes cria três valores que podem discordar. Guarde o conjunto original e o filtro; derive o restante. Outro erro é colocar o array dentro de cada filho, fazendo formulário e lista viverem em versões diferentes da aplicação.

No laboratório, faça os oito testes e altere o estado inicial para vazio. O projeto está concluído quando o resultado é previsível em cada filtro, não perde itens após atualizações e comunica o vazio. Uma próxima evolução pode persistir no localStorage ou integrar uma API, sempre tratando carregamento, erro e dados remotos como estados explícitos.

Laboratório ao vivo

Painel final de tarefas

Cadastre, conclua, filtre e remova tarefas. Depois crie um estado vazio e confirme que a interface explica o resultado em vez de mostrar uma lista muda.

Pronto para testar
Resultado

Pare e pense

Onde deve morar o estado compartilhado pelo formulário, resumo e lista?

Escolha uma resposta
Missão da aula

Faça sem copiar

Entregue o painel com adicionar, concluir, remover e filtrar. A missão passa nos oito testes manuais descritos na aula, inclusive teclado e estado vazio.

Fontes para consultar

Terminou a missão?

Marque apenas quando você conseguir explicar o conceito e concluir o desafio. O progresso fica salvo somente neste navegador.

Voltar ao curso e ver seu progresso →