Estado e Gerenciamento de Eventos no React | [SIS]ANO2C3B2S8A3
[ SIS ] ANO2C3B2S8A3·Unidade 2 · Componente 3 · Semana 8·Ensino Médio Técnico — Desenvolvimento de Sistemas

Construa uma lista de tarefas completa com React — adicionar, remover, marcar como concluída e persistir com localStorage. Três hooks, quatro funções, um produto funcional.

ReactuseState useEffectlocalStorage SEDUC-SPTo-Do List

Por que toda aplicação real começa com uma lista de tarefas?

A To-Do List não é um projeto trivial — é a menor aplicação que concentra os três desafios fundamentais de qualquer interface interativa: gerenciar estado (o que está na lista?), reagir a eventos (o usuário clicou em “remover”) e persistir dados (a lista precisa sobreviver ao F5). Quem constrói uma To-Do List completa em React com localStorage entendeu os fundamentos que estão presentes em sistemas de e-commerce, dashboards e painéis de gestão.

Esta aula fecha o ciclo da semana 8: depois de componentes (8.1) e ciclo de vida (8.2), agora é o estado e os eventos que tornam a interface reativa ao usuário — e útil além do ambiente de desenvolvimento.

Custo concreto de não dominar estado e eventos: interfaces sem gerenciamento adequado de estado criam dados inconsistentes, re-renders desnecessários e bugs silenciosos. Um campo de input que não limpa após o submit, uma lista que perde dados ao recarregar — todos são falhas de state e localStorage. Em qualquer entrevista técnica de front-end, esses conceitos são triagem obrigatória.

Quais são os conceitos centrais desta aula?

≡ definição · useState

Hook que adiciona estado local mutável a componentes funcionais. Retorna par [valor, setter] — toda chamada ao setter dispara re-render com o novo valor. Imutabilidade é obrigatória: nunca alterar o array ou objeto diretamente.

≡ definição · localStorage

API do navegador que armazena pares chave-valor como strings sem prazo de expiração. Persiste entre sessões. Requer JSON.stringify para salvar objetos e JSON.parse para recuperar — suporta apenas tipos primitivos nativamente.

Como funciona cada operação da To-Do List em código?

operação · adicionar

addTask

Cria novo objeto { text, completed: false } e usa spread operator para retornar novo array sem mutar o estado original.

operação · completar

toggleComplete

Usa map para percorrer — quando o índice bate, retorna spread do item com completed invertido. Todos os outros são retornados inalterados.

operação · remover

removeTask

Usa filter para retornar novo array excluindo apenas o item com o índice fornecido. Um liner que resolve a operação completa.

FUNÇÃO / HOOKO QUE FAZPADRÃO DE IMUTABILIDADE
useState([])Inicializa lista de tarefas como array vazioNunca push() direto — sempre setter com novo array
addTask()Acrescenta nova tarefa ao array[…tasks, newTask] — spread cria novo array
toggleComplete(index)Inverte o campo completed do item no índicetasks.map() — retorna novo array completo
removeTask(index)Exclui item pelo índicetasks.filter() — retorna novo array sem o item
useEffect([tasks])Salva no localStorage sempre que tasks mudaJSON.stringify — serializa para string
useEffect([])Carrega do localStorage na montagemJSON.parse — desserializa de string

Como implementar o App.js completo da To-Do List?

Template 1 — Lógica de estado e funções

App.js — estado, funções e localStorage
import React, { useState, useEffect } from 'react';
import './App.css';

function App() {
  const [tasks, setTasks] = useState([]);
  const [input, setInput] = useState('');

  // Carrega do localStorage na montagem
  useEffect(() => {
    const saved = JSON.parse(localStorage.getItem('tasks'));
    if (saved) setTasks(saved);
  }, []); // ← [] = executa apenas uma vez na montagem

  // Salva no localStorage sempre que tasks muda
  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]); // ← executa toda vez que tasks muda

  // Adiciona nova tarefa — spread cria novo array (imutabilidade)
  const addTask = () => {
    const newTask = { text: input, completed: false };
    setTasks([...tasks, newTask]);
    setInput('');
  };

  // Alterna completed — map retorna novo array
  const toggleComplete = (index) => {
    const updated = tasks.map((t, i) =>
      i === index ? { ...t, completed: !t.completed } : t
    );
    setTasks(updated);
  };

  // Remove tarefa — filter retorna novo array sem o item
  const removeTask = (index) => {
    setTasks(tasks.filter((_, i) => i !== index));
  };

Template 2 — Renderização JSX com eventos

App.js — return JSX com eventos inline
  return (
    <div className="App">
      <h1>To-Do List</h1>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Nova tarefa"
      />
      <button onClick={addTask}>Adicionar</button>
      <ul>
        {tasks.map((task, index) => (
          <li key={index} className="todo-item">
            <span
              className={task.completed ? 'completed' : ''}
              onClick={() => toggleComplete(index)}
            >
              {task.text}
            </span>
            <button onClick={() => removeTask(index)}>Remover</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
Por que dois useEffect? Carregar e salvar têm ciclos diferentes. O de carregamento precisa executar apenas uma vez — na montagem, com []. O de salvamento precisa executar sempre que tasks muda — com [tasks]. Colocar tudo em um único useEffect ou misturar as dependências criaria comportamentos incorretos: dados seriam sobrescritos ao carregar ou perdidos em salvamentos tardios.

Como este projeto aparece em contexto pedagógico real?

! caso real · projeto To-Do List — dupla · 45 minutos

Quatro etapas do roteiro oficial

  • Etapa 1 — Setup: npx create-react-app todo-list, limpeza do App.js, criação do App.css com estilos básicos.
  • Etapa 2 — Lógica: implementar estados tasks e input, funções addTask, toggleComplete e removeTask.
  • Etapa 3 — Persistência: dois useEffect para salvar e carregar do localStorage com JSON.stringify/parse.
  • Etapa 4 — Renderização: JSX com mapeamento da lista, eventos inline e classe condicional completed.
→ expansão estratégica · 4 frentes

Da To-Do List a produto real e ativo pedagógico

{ }Curso Técnico

Evoluir a To-Do List para banco de dados real com Firebase Realtime DB ou Supabase — sincronização entre dispositivos, autenticação de usuário.

Blog / Autoridade

Artigo “De To-Do List a produto: como React + localStorage vira o MVP que muitos dev sêniors não constroem em entrevistas”.

[ ]Cultura Maker

Projeto integrado: lista de tarefas colaborativa do laboratório — quais computadores precisam de manutenção, tarefas da semana, equipes de limpeza.

>_Formação Docente

Oficina: como ensinar estado e eventos com localStorage sem backend — qualquer navegador moderno é o servidor desta aula.

Síntese: useState cria e gerencia o estado para adicionar, remover e atualizar tarefas. useEffect + localStorage garante que as tarefas persistam após recarregamento. A manipulação de eventos com addTask, removeTask e toggleComplete demonstra o fluxo completo de interação do usuário com o estado da aplicação.

Perguntas frequentes sobre estado, eventos e localStorage no React

O que é o hook useState no React?
useState é um hook que adiciona estado local a componentes funcionais. Recebe o valor inicial e retorna um array com o valor atual e a função setter. Toda chamada ao setter dispara re-render do componente com o novo valor.
Como sincronizar estado React com o localStorage?
Use dois useEffect: um com [tasks] para salvar com localStorage.setItem e JSON.stringify sempre que tasks muda; outro com [] para carregar com localStorage.getItem e JSON.parse apenas na montagem do componente.
Como funciona o gerenciamento de eventos no React?
Eventos em React são declarados via props JSX em camelCase: onClick, onChange, onSubmit. Para listas, o índice é passado via arrow function: onClick={() => removeTask(index)}. Isso garante que o handler receba o índice correto sem execução imediata.
Por que usar spread operator ao atualizar arrays no state React?
React requer imutabilidade no state. Para adicionar: […tasks, newTask] cria um novo array. Para remover: tasks.filter() retorna novo array. Para atualizar: tasks.map() retorna novo array com o item modificado. Mutar diretamente não dispara re-render.
O que é localStorage e como ele persiste dados no navegador?
localStorage é uma API do navegador que armazena dados em pares chave-valor sem prazo de expiração. Os dados persistem mesmo após fechar o navegador. Suporta apenas strings — por isso JSON.stringify e JSON.parse são necessários para objetos e arrays.

Acesse todos os materiais do Curso Técnico

Slides, roteiros de projeto, hooks avançados e reflexões sobre ensino de React na escola pública.

→ professorcomia.com.br

Estado e eventos como o DNA de qualquer interface interativa

Todo produto digital que responde ao usuário — de um botão de like a um carrinho de compras — implementa exatamente os mesmos conceitos desta aula: um array de estado, funções que atualizam imutavelmente, eventos que disparam essas funções e persistência que mantém o contexto entre sessões. Quem construiu a To-Do List desta aula construiu, em miniatura, a mesma arquitetura do Trello, do Notion e de qualquer sistema de gestão de tarefas em produção.

// código da aula: [SIS]ANO2C3B2S8A3 · unidade 2 · componente 3 · semana 8
// estado e gerenciamento de eventos no react · seduc-sp · 2024
// publicado em: professorcomia.com.br · diariodeumpoed.com.br · poed

>_ slides da aula

[SIS]ANO2C3B2S8A3 · estado e gerenciamento de eventos · 2º ano · semana 8 · aula 3

slide 01
abertura

>_ Frameworks e Bibliotecas JavaScript — Aula 3

FREQUÊNCIA — registre sua presença no AVA antes de iniciar
  • Código: [SIS]ANO2C3B2S8A3 — Unidade 2 · Componente 3 · Semana 8
  • Fechando a semana: componentes (8.1) → ciclo de vida (8.2) → estado e eventos (8.3)
  • Produto desta aula: To-Do List completa com useState, useEffect e localStorage
? pergunta provocadora — antes de qualquer slide Você usa algum app de lista de tarefas? Quando você fecha e reabre, as tarefas continuam lá. Como o app “lembra” o que você escreveu — se o React não tem servidor nesta aula? Qual tecnologia do próprio navegador resolve isso?
slide 02
objetivos

[ ] O que vamos construir hoje

conceitual

Compreender como useState gerencia arrays de objetos e como useEffect sincroniza estado com localStorage para persistência entre sessões.

procedimental

Implementar a To-Do List completa — addTask, toggleComplete e removeTask — com persistência automática via localStorage.

atitudinal

Desenvolver pensamento de produto: construir uma interface que seja funcional, persistente e utilizável pelo usuário final — não apenas pelo desenvolvedor.

≡ recursos · laboratório de informática · Node.js + npm (ou StackBlitz) · caderno · 50 minutos
slide 03
problema_gerador

! A interface que esquece tudo

sem persistência

O usuário adiciona 10 tarefas importantes ao longo do dia. Fecha o navegador acidentalmente. Reabre o app — a lista está completamente vazia. O useState sozinho não sobrevive ao reload da página.

com localStorage + useEffect

Cada mudança na lista dispara o useEffect que serializa e salva no localStorage. Ao reabrir, o segundo useEffect carrega os dados salvos. A lista está exatamente onde o usuário deixou.

? 5 minutos — no caderno Pense em 3 apps que você usa que precisam “lembrar” dados entre sessões. Para cada um, tente imaginar: os dados ficam no servidor ou no próprio dispositivo? Qual a diferença — e qual solução é mais rápida?
slide 04
conceito

useState — gerenciando arrays de objetos

  • Estado da lista: const [tasks, setTasks] = useState([]) — array de objetos { text, completed }
  • Estado do input: const [input, setInput] = useState('') — string controlada sincronizada com o campo
  • Imutabilidade obrigatória: nunca tasks.push() — sempre setTasks([...tasks, newTask])
  • Por que dois estados? input e tasks têm ciclos de vida diferentes — input reseta ao adicionar, tasks persiste
01

O que acontece se você fizer tasks.push(newTask) e depois setTasks(tasks)? React vai renderizar a tarefa nova? Por quê o spread é obrigatório aqui?

02

Por que cada tarefa é um objeto { text, completed } em vez de apenas uma string? O que o campo completed habilita que uma simples string não habilita?

slide 05
conceito

{ } As 3 funções que controlam a lista

  • addTask: [...tasks, { text: input, completed: false }] + limpa input — spread preserva tarefas existentes
  • toggleComplete: tasks.map((t,i) => i===index ? {...t, completed: !t.completed} : t) — spread do objeto + inversão
  • removeTask: tasks.filter((_,i) => i !== index) — um liner que exclui pelo índice
as 3 funções — padrão imutável
const addTask = () => { setTasks([...tasks, {text:input,completed:false}]); setInput(''); };
const toggleComplete = (i) => setTasks(tasks.map((t,j) => j===i?{...t,completed:!t.completed}:t));
const removeTask = (i) => setTasks(tasks.filter((_,j) => j !== i));
01

Em toggleComplete, a linha usa {...t, completed: !t.completed}. O que o spread faz aqui? O que seria perdido se você apenas retornasse { text: t.text, completed: !t.completed }?

slide 06
conceito

useEffect + localStorage — persistência automática

  • Salvar: useEffect(() => localStorage.setItem('tasks', JSON.stringify(tasks)), [tasks]) — dispara em toda mudança
  • Carregar: useEffect(() => { const s = JSON.parse(localStorage.getItem('tasks')); if(s) setTasks(s) }, []) — uma vez na montagem
  • Por que JSON? localStorage só aceita strings — JSON.stringify serializa o array, JSON.parse desserializa
  • Ordem importa: o useEffect de carregamento com [] DEVE estar antes do de salvamento — caso contrário, salva vazio antes de carregar
01

Se você invertesse a ordem dos dois useEffect — salvamento antes de carregamento — o que aconteceria na primeira vez que a página carrega? As tarefas seriam sobrescritas por um array vazio?

02

O que acontece com o localStorage se o usuário abrir a To-Do List em dois abas ao mesmo tempo e adicionar tarefas diferentes em cada uma? localStorage é compartilhado entre abas do mesmo domínio?

slide 07
caderno

Glossário — Estado e Eventos no React

  • useState([]) inicializa estado como array vazio — setTasks cria novo array a cada operação
  • spread operator […tasks, item] cria novo array — obrigatório para imutabilidade no React
  • addTask acrescenta objeto {text, completed:false} ao array via spread
  • toggleComplete map() inverte completed no índice alvo — todos os outros inalterados
  • removeTask filter() retorna novo array excluindo o índice fornecido
  • localStorage API do navegador para persistência sem servidor — aceita apenas strings
  • JSON.stringify serializa array para string antes de salvar no localStorage
  • JSON.parse desserializa string do localStorage de volta para array de objetos
? escreva no caderno antes de avançar
  • Por que tasks.push(newTask) não funciona para atualizar a interface — mesmo que mute o array? O que React “não vê”?
  • Se localStorage guarda strings e nossas tasks são objetos, qual é o risco de fazer localStorage.getItem('tasks') sem o JSON.parse?
slide 08
contexto_atividade · aula prática

{ } To-Do List — produto funcional do zero

  • Cenário: construir uma aplicação de lista de tarefas em que o usuário pode adicionar, marcar como concluída, remover e ter tudo salvo automaticamente
  • Requisitos técnicos: useState (tasks + input), useEffect (salvar + carregar), três funções de evento, CSS para completed
  • Formato: dupla · 45 minutos · código de programação como entrega
  • Ferramenta: npx create-react-app todo-list ou StackBlitz (sem instalação)
01

Antes de codificar: esboce no papel a estrutura de dados de uma tarefa. Quais campos são necessários além de text e completed? Uma ID? Uma data de criação? Isso mudaria a addTask?

02

O input não tem validação no roteiro. O que acontece se o usuário clicar em “Adicionar” com o campo vazio? Como você preveniria isso com uma linha de código?

slide 09
planejamento

Planejamento — estrutura antes do código

ELEMENTOTIPOFINALIDADEDEPENDÊNCIA
tasksuseState([])Array de objetos {text, completed}setTasks em cada operação
inputuseState(”)Valor atual do campo de textosetInput no onChange e após addTask
addTask()funçãoAcrescenta tarefa e limpa inputtasks, input, setTasks, setInput
toggleComplete(i)funçãoInverte completed pelo índicetasks, setTasks, index
removeTask(i)funçãoRemove tarefa pelo índicetasks, setTasks, index
useEffect [tasks]hookSalva no localStoragetasks
useEffect []hookCarrega do localStorage
? antes de codificar Os dois useEffect precisam ser declarados em qual ordem no arquivo para garantir que o carregamento aconteça antes do primeiro salvamento?
slide 10
algoritmo

>_ Fluxo lógico — 4 etapas do roteiro

  • Etapa 1 — Setup: npx create-react-app todo-list → limpar App.js → criar App.css com classes todo-item e completed
  • Etapa 2 — Lógica: declarar estados → implementar as 3 funções → testar no console antes do JSX
  • Etapa 3 — Persistência: adicionar os 2 useEffect (carregamento PRIMEIRO, salvamento DEPOIS)
  • Etapa 4 — Renderização: JSX com input controlado, botão addTask, map da lista, onClick nos spans e botões
fluxo crítico — inputs controlados
// Input controlado: value + onChange sincronizados com state
<input value={input} onChange={(e) => setInput(e.target.value)} />
// onClick em lista: arrow function passa o índice correto
onClick={() => removeTask(index)}
slide 11
implementacao

Implementação — VSCode ou StackBlitz

  • Sem instalação: stackblitz.com → New Project → React — funciona em qualquer laboratório
  • Com instalação: npx create-react-app todo-list → substitua App.js e crie App.css
  • Teste de persistência: adicione 3 tarefas → recarregue F5 → as tarefas devem reaparecer — se não, verifique a ordem dos useEffect e o JSON.parse
  • DevTools: Application → Local Storage → veja a chave “tasks” com o JSON serializado em tempo real
01

Abra o DevTools → Application → Local Storage. Adicione uma tarefa — o JSON muda em tempo real? Qual a diferença visual entre uma tarefa completed:false e completed:true no localStorage?

02

Tente colocar onClick={removeTask(index)} em vez de onClick={() => removeTask(index)}. O que acontece? Por que a arrow function é obrigatória em listas?

slide 12
testes

! Testes — 3 cenários obrigatórios

funcionamento normal

Adicionar 3 tarefas, marcar 1 como concluída, remover 1

→ lista com 2 itens, 1 riscado, input limpo após cada ação

persistência

Adicionar tarefas → recarregar F5 → verificar

→ tarefas reaparecem com estado correto (completed preservado)

edge cases

Adicionar tarefa vazia · remover todas · DevTools JSON

→ validar comportamento e verificar localStorage no DevTools

? análise final da dupla — registre no AVA Quais foram os principais desafios durante o desenvolvimento? O que você mudaria no código para tornar a aplicação mais robusta — adicionar validação de input vazio, limite de tarefas, possibilidade de editar o texto de uma tarefa?
slide 13
entrega_ava

Entrega — O que enviar no AVA

o que enviar
  • Código completo do App.js com todas as funcionalidades
  • Arquivo App.css com estilos das classes todo-item e completed
  • Print da aplicação funcionando com tarefas na tela
  • Reflexão escrita: principais desafios e o que você mudaria
critérios de avaliação
  • Adicionar, remover e marcar como concluído funcionando
  • Persistência com localStorage — tarefas sobrevivem ao F5
  • Input controlado limpa após adicionar tarefa
  • Reflexão fundamentada sobre desafios encontrados
⌨ entrega · App.js + App.css + print + reflexão escrita · AVA · dupla · prazo: conforme calendário da turma
slide 14
síntese

Então ficamos assim…

useState — gerenciar tarefas

Usar useState para criar e gerenciar o estado de uma aplicação React — adicionar, remover e atualizar tarefas em uma lista com imutabilidade.

localStorage — persistência

Sincronizar dados com localStorage via useEffect — garantindo que as tarefas persistam mesmo após o recarregamento da página.

{ }Manipulação de eventos

Adicionar, remover e marcar como concluídas usando funções que atualizam o estado de forma imutável com spread, map e filter.

← aula anterior

Componentes e ciclo de vida (8.2) — componentDidMount, componentDidUpdate e componentWillUnmount com o projeto UserTracker.

→ próxima semana

Semana 9: Estilização e layout — CSS-in-JS, styled-components e Tailwind para organização visual em escala com React.