Um formulário React guarda o valor de cada campo no estado: o input mostra value={state} e o atualiza com onChange. Quando o usuário envia, um handler de onSubmit chama e.preventDefault() para impedir o recarregamento da página e depois usa os valores que já estão no estado.
Digite um nome e aperte Enter ou clique no botão. Depois apague a linha e.preventDefault();: a prévia mostra os campos que o navegador teria enviado em um envio nativo, o que em uma página real também a recarregaria.
Um input cujo valor vem do estado se chama input controlado. A alternativa, deixar o DOM guardar o valor e lê-lo depois, é tratada em componentes controlados vs não controlados.
Inputs de texto e textarea
Todo campo de texto funciona do mesmo jeito: value vem do estado e onChange lê e.target.value. Um <textarea> segue o mesmo padrão. No HTML o texto dele vai entre as tags, mas no React você o passa como value.
Como é você que define o valor, dá para mudar o que o usuário digitou antes de aparecer: o campo de título transforma cada letra em maiúscula. O contador de caracteres atualiza a cada tecla porque body é estado e cada mudança renderiza de novo.
Select
Um <select> recebe value no próprio elemento select, não selected em uma option. e.target.value é o value da opção escolhida.
Para um select múltiplo, adicione multiple e passe um array como value. Leia as escolhas com Array.from(e.target.selectedOptions, (o) => o.value).
Checkbox e botões de rádio
Um checkbox é controlado com checked, não value, e o handler dele lê e.target.checked, que é true ou false. Botões de rádio de um mesmo grupo compartilham um name; cada um fica marcado quando o estado é igual ao valor dele.
Ler e.target.value em um checkbox é um erro comum: em um checkbox sem atributo value ele retorna a string "on" esteja a caixa marcada ou não.
Um objeto de estado para vários campos
Um formulário com dez campos não precisa de dez chamadas de useState e dez handlers. Mantenha um objeto, dê a cada input um name que corresponda a uma chave e escreva um único handler que use o nome como chave calculada: [e.target.name].
O handler confere o tipo do input para que o checkbox guarde um booleano e os outros guardem texto. Adicione um campo acrescentando uma chave ao objeto inicial e um input com o mesmo name; o handler não precisa mudar. O spread mantém todos os outros campos (veja atualizar arrays e objetos).
Enviando um formulário
Coloque a lógica de envio no onSubmit do formulário, não no onClick do botão. O onSubmit também dispara quando o usuário aperta Enter em um campo de texto, o que um handler de clique perderia. Um <button> dentro de um formulário é um botão de envio por padrão; dê type="button" aos outros botões para que não enviem.
Abra o console, digite uma senha e aperte Enter: o formulário registra o objeto inteiro. Clicar em "Clear" não registra nada, porque ele é type="button". Remova esse atributo e o Clear também passa a enviar.
Validação e exibição de erros
Uma validação simples é uma função que recebe os valores e retorna um objeto de mensagens. Execute-a no envio, guarde o resultado no estado e mostre cada mensagem abaixo do campo correspondente. Desabilite o botão de envio enquanto uma requisição estiver em andamento para que um clique duplo não envie o formulário duas vezes.
Clique em Save com os campos vazios para ver as duas mensagens e depois preencha-os. saveProfile faz o papel de uma requisição real: espera 800 milissegundos e resolve. O atributo noValidate desliga os balões de validação do próprio navegador para que só as suas mensagens apareçam; tire-o e adicione required a um input para comparar.
Os erros também podem ser calculados durante a renderização em vez de guardados, por exemplo const emailError = form.email && !form.email.includes('@'), que mostra a mensagem enquanto o usuário digita. Escolha o momento que combina com o formulário: no envio é mais tranquilo, durante a digitação dá um retorno mais rápido.
Form actions no React 19
O React 19 adiciona outra forma de lidar com um formulário. Passe uma função para a prop action do formulário. O React evita o recarregamento da página para você e chama a função com um objeto FormData que contém todos os campos que têm um name. Os campos nem precisam de estado: eles são não controlados, e o React os limpa depois que a action termina.
Digite o seu próprio comentário e publique. A lista mostra o seu texto, e o input volta para Nice docs: depois que a action resolve, o React reinicia os campos não controlados para o defaultValue deles. A action pode ser assíncrona, e enquanto ela roda o React trata a atualização como uma transição.
Para mostrar um estado pendente ou um erro vindo da action, envolva-a com useActionState, que retorna o resultado mais recente, uma action envolvida para o formulário e um indicador isPending. Um botão bem dentro do formulário também pode ler o estado pendente com useFormStatus de react-dom:
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}
O useFormStatus não é carregado nos editores daqui, então aparece só neste bloco estático. Ele precisa ser chamado em um componente renderizado dentro do <form>, não no componente que renderiza o formulário.
Estado controlado com onSubmit e form actions com FormData funcionam no React 19. Campos controlados combinam com formulários que reagem enquanto você digita (validação ao vivo, um contador de caracteres, campos que dependem uns dos outros). Actions combinam com formulários em que você só precisa dos valores quando o usuário envia.
Perguntas frequentes
Como pego o valor de um input no React?
Guarde-o no estado e atualize-o pelo onChange: <input value={name} onChange={e => setName(e.target.value)} />. Assim o texto atual está sempre em name. Para checkboxes, leia e.target.checked em vez de e.target.value.
Como impeço um formulário de recarregar a página no React?
Chame e.preventDefault() no início do handler de onSubmit. Sem isso, o navegador faz um envio normal de formulário, que navega e recarrega a página.
Como lido com vários inputs com um único estado no React?
Mantenha um objeto no estado, dê a cada input um name que corresponda a uma chave e use um único handler com uma chave calculada: setForm({ ...form, [e.target.name]: e.target.value }).
Por que um textarea usa value em vez de children no React?
No HTML, o texto de um textarea fica entre as tags, mas no React você o passa como value (ou defaultValue), do mesmo jeito que um input. Assim todos os campos de texto de um formulário React funcionam igual.
O que são form actions no React 19?
No React 19 você pode passar uma função para <form action={fn}>. O React evita o recarregamento da página, chama a função com o FormData do formulário e reinicia os campos não controlados quando termina. O useActionState adiciona por cima um indicador de pendência e um resultado.