Menu

Componentes controlados vs não controlados no React

Um input controlado recebe o valor do estado do React e só muda pelo onChange. Um input não controlado guarda o próprio valor no DOM, e você o lê com uma ref ou com FormData. Aprenda a diferença, os avisos comuns e quando usar cada um.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um componente controlado é um elemento de formulário cujo valor mora no estado do React: você passa value e atualiza o estado no onChange, então é o React que decide o que o input mostra. Um componente não controlado guarda o valor no DOM, como no HTML puro: você passa um defaultValue inicial e lê o valor atual depois por uma ref ou com FormData.

Digite nos dois inputs. O parágrafo acompanha o primeiro a cada tecla, porque cada mudança define o estado e renderiza de novo. O segundo input muda só no DOM; o React não renderiza, e você só vê o valor dele quando "Read both" o pede.

Inputs controlados

Com um input controlado, o estado do React é a única fonte da verdade. O fluxo a cada tecla é:

  1. O usuário digita, e o navegador dispara um evento de input.
  2. O seu handler de onChange lê e.target.value e chama o setter.
  3. O React renderiza de novo e escreve o novo estado de volta no value do input.

Como o seu código fica no meio desse ciclo, você pode fazer coisas que um input não controlado não consegue: rejeitar ou remodelar caracteres, mostrar o valor em outro lugar, desabilitar um botão até o valor ser válido ou definir o valor pelo código (um botão "Clear", uma sugestão que o usuário clica).

Tente digitar letras: elas nunca aparecem, porque o handler remove tudo o que não é dígito antes de definir o estado. O botão Verify fica desabilitado até haver exatamente seis dígitos.

Controlado é o que os exemplos de formulário da página de formulários usam, e é a escolha padrão na maior parte do código React.

Inputs não controlados

Um input não controlado funciona como HTML puro. O React o renderiza uma vez com defaultValue (ou defaultChecked para checkboxes e rádios) e depois o deixa em paz. Para ler o valor, guarde uma ref para o elemento ou leia todos os campos com nome do formulário no envio, com FormData.

Não há estado para os campos nem onChange. Os valores só são coletados quando o formulário é enviado. Desmarque o checkbox e envie de novo: a chave subscribe desaparece, porque um checkbox desmarcado simplesmente não faz parte do FormData.

Mudar o defaultValue depois da primeira renderização não faz nada no input, já que o React só o aplica quando o elemento é criado. Para reiniciar um formulário não controlado, chame e.target.reset() ou dê ao formulário uma nova key para que o React crie inputs novos.

Um input em que não dá para digitar

Se você passa value mas nenhum onChange, o input é controlado, mas nada nunca atualiza o estado. O React escreve o mesmo valor de volta depois de cada tecla, então o input parece congelado.

Tente digitar em cada campo. O primeiro não muda. O segundo é a correção para um input controlado, e enquanto você digita nele, o primeiro e o último campos o acompanham, porque os três leem o mesmo estado. O terceiro aceita digitação porque é não controlado. O quarto diz de propósito que não pode ser editado.

Em desenvolvimento, o React registra um aviso para o primeiro input: "You provided a value prop to a form field without an onChange handler". A prévia roda como um build de produção, então você não vai ver esse aviso aqui, mas vai vê-lo no seu próprio servidor de desenvolvimento.

Mudando de não controlado para controlado

O React decide se um input é controlado olhando para value. Se value é undefined ou null, o input é não controlado. Então um input cujo value começa como undefined e depois vira uma string passa de não controlado para controlado ao longo da vida, e o React avisa em desenvolvimento: "A component is changing an uncontrolled input to be controlled".

O aviso aponta para um bug real. Voltar no sentido contrário, de uma string para undefined, torna o input não controlado de novo, e o React para de escrever nele.

Digite algo nos dois inputs e aperte cada botão Clear. O segundo input fica vazio. O primeiro mantém o texto: definir o estado como undefined o tornou não controlado, então o React não controla mais o que ele mostra.

A origem comum do undefined são dados que ainda não carregaram (value={user.name} antes de o usuário chegar) ou um campo de objeto que ainda não existe. Duas correções:

const [name, setName] = useState('');   // start with a string
<input value={user?.name ?? ''} />       // or fall back to one

Para checkboxes, a mesma regra vale para checked: comece com false, não undefined.

Inputs de arquivo são sempre não controlados

Um <input type="file"> não pode ser controlado, porque os navegadores não deixam uma página definir o valor dele (uma página que pudesse escolher arquivos do seu disco seria uma falha de segurança). Leia os arquivos escolhidos em e.target.files no onChange, ou por uma ref quando o formulário for enviado.

Escolha um ou dois arquivos do seu computador; nada é enviado, o exemplo só lista nomes e tamanhos. O único valor que você pode definir em um input de arquivo é a string vazia, e é assim que o botão Clear o reinicia.

Quando escolher cada um

ControladoNão controlado
O valor mora noEstado do ReactDOM
Definir o valor inicialvalue / checkeddefaultValue / defaultChecked
Ler o valorA qualquer momento, no estadoCom uma ref, ou FormData no envio
Renderiza a cada teclaSimNão
Mudar o valor pelo códigoDefinir o estadoEscrever no DOM por uma ref
Bom paraValidação ao vivo, formatação, campos dependentesFormulários simples lidos no envio, inputs de arquivo, form actions

Escolha controlado quando a tela precisa reagir ao valor enquanto o usuário digita. Escolha não controlado quando você só precisa dos valores no final, que também é como funcionam as form actions do React 19: <form action={fn}> entrega à sua função um FormData e reinicia os campos não controlados depois.

Renderizar a cada tecla parece caro, mas para um formulário normal não é problema. Só vira problema quando uma árvore grande e lenta renderiza de novo junto com o input; aí mantenha o estado perto do input ou mova a parte lenta para um componente próprio.

As mesmas duas palavras descrevem os seus próprios componentes. Um componente que recebe o valor e um onChange do pai pelas props é controlado pelo pai; um que guarda o próprio estado é não controlado. Mover o estado para cima para tornar um componente controlado é o assunto de elevar o estado.

Perguntas frequentes

O que é um componente controlado no React?

Um elemento de formulário cujo valor atual vem do estado do React, definido com value (ou checked) e atualizado pelo onChange. O estado do React é a única fonte da verdade, então você sempre sabe o valor e pode mudá-lo pelo código.

O que é um componente não controlado no React?

Um elemento de formulário que guarda o próprio valor no DOM, como no HTML puro. Você dá a ele um valor inicial com defaultValue (ou defaultChecked) e lê o valor atual quando precisa, por uma ref ou com FormData no envio.

Por que não consigo digitar no meu input React?

Você passou value sem um onChange. O input é controlado, então o React continua recolocando o valor do estado depois de cada tecla. Adicione um onChange que atualize o estado, troque para defaultValue ou adicione readOnly se ele realmente não deve mudar.

Como corrijo "A component is changing an uncontrolled input to be controlled"?

O value do input começou como undefined (ou null) e depois virou uma string. Garanta que ele seja sempre uma string: comece o estado com '' ou escreva value={name ?? ''}.

Devo usar inputs controlados ou não controlados?

Use inputs controlados quando a UI reage ao valor enquanto o usuário digita: validação, formatação, um botão desabilitado, campos que dependem uns dos outros. Use inputs não controlados quando você só precisa dos valores no envio, e para inputs de arquivo, que são sempre não controlados.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR