React
Formularios controlados y no controlados
Compara formularios controlados y no controlados en React, incluyendo value, defaultValue, refs, estado local y trade-offs de validación y rendimiento.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
React
Compara formularios controlados y no controlados en React, incluyendo value, defaultValue, refs, estado local y trade-offs de validación y rendimiento.
React posee el valor y lo actualiza en cada evento.
const [email, setEmail] = useState("");
<input
value={email}
onChange={(event) => setEmail(event.target.value)}
/>Permite validación inmediata, formato y coordinación con otros elementos.
El DOM conserva el valor.
<form action={submitForm}>
<input name="email" defaultValue="" />
</form>Puede leerse con FormData al enviar.
Un input no debería pasar de undefined a string bajo value. Inicializa con "" si será controlado.
<input
type="checkbox"
checked={accepted}
onChange={(event) => setAccepted(event.target.checked)}
/>No todo campo necesita estado individual si solo importa al enviar. Elige control según la interacción requerida.
value sin onChange.event.target.value de un checkbox.defaultValue y value.Controlado ofrece sincronización continua; no controlado deja el dato en el DOM hasta necesitarlo.
La diferencia central es quién conserva el estado actual:
controlado → React state / parent
no controlado→ DOM inputNo significa “bueno frente a malo”. Cada modelo optimiza necesidades distintas.
function EmailField() {
const [email, setEmail] = useState("");
return (
<label>
Correo
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
</label>
);
}value.onChange recibe el nuevo valor.Si omites onChange, el campo queda de solo lectura.
function ContactForm() {
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const email = String(formData.get("email") ?? "");
// validar y enviar
}
return (
<form onSubmit={handleSubmit}>
<label>
Correo
<input name="email" type="email" defaultValue="" required />
</label>
<button>Enviar</button>
</form>
);
}El DOM mantiene el valor y lo lees al enviar. Esto reduce state cuando no necesitas reaccionar a cada pulsación.
value: fuente controlada en cada render.defaultValue: valor inicial del DOM.Actualizar defaultValue después del montaje no reemplaza el texto actual. Para reiniciar un input no controlado, usa form.reset(), una Action compatible o cambia deliberadamente la identidad.
<input value={user?.name} />Si empieza como undefined, es no controlado; cuando aparece string, se vuelve controlado. Inicializa:
<input value={user?.name ?? ""} onChange={...} />Elige el modo durante toda la vida de la instancia.
<input
type="checkbox"
checked={accepted}
onChange={(event) => setAccepted(event.target.checked)}
/>Los radios comparten name y el controlado compara su value.
<select
multiple
value={selectedIds}
onChange={(event) => {
const values = Array.from(event.currentTarget.selectedOptions, (option) => option.value);
setSelectedIds(values);
}}
/>El valor de archivos es administrado por el navegador; normalmente es no controlado. Lee files o FormData.
Una librería de formularios puede usar inputs no controlados internamente y ofrecer una API declarativa.
No guardes cada error si puede calcularse:
const emailError = email !== "" && !isValidEmail(email)
? "Ingresa un correo válido"
: null;State adicional puede representar cuándo mostrarlo (touched o intento de submit), no necesariamente el mensaje derivado.
Un controlado renderiza al cambiar. Eso suele ser barato. Si un formulario completo se vuelve lento:
No cambies a no controlado sin comprender validación y ownership.
Controlado:
setForm(initialForm);No controlado:
formRef.current?.reset();Después de un submit exitoso, no limpies antes de confirmar. Mantén los datos si el servidor devuelve error.
label real.name para envío.aria-describedby.aria-invalid cuando corresponda.defaultValue solo inicializa.value necesita onChange?defaultValue después de escribir?Validación, envío y estados del formulario modela el flujo completo más allá del valor de cada campo.