Working with Forms & User InputEntry 3 of 10

Uncontrolled Inputs with Refs

An uncontrolled input keeps its own value in the DOM. React reads it only when needed, through a ref.

react.devuseRef
angular.devviewChild

Notes

  • No state and no onChange: the browser manages the value.
  • Read the values in the submit handler with ref.current.value.
  • Set a starting value with defaultValue (or defaultChecked), not value.
  • Fine for small forms, but one ref per input gets repetitive. FormData (Entry 4) reads every field at once without refs.

What / Why / When

What

Reading input values directly from the DOM.

Why

Less code and no re-render on every keystroke.

When

You only need the values on submit.

Use case

A login form with two fields.

Code compare

Highlight matching lines:
Reactlogin.tsx
import { useRef, type FormEvent } from "react";​export default function Login() {  const emailRef = useRef<HTMLInputElement>(null);  const passwordRef = useRef<HTMLInputElement>(null);​  function handleSubmit(event: FormEvent<HTMLFormElement>) {    event.preventDefault();    const email = emailRef.current!.value;    const password = passwordRef.current!.value;    console.log(email, password);  }​  return (    <form onSubmit={handleSubmit}>      <label htmlFor="email">Email</label>      <input id="email" type="email" ref={emailRef} defaultValue="" />​      <label htmlFor="password">Password</label>      <input id="password" type="password" ref={passwordRef} />​      <button type="submit">Login</button>    </form>  );}

A template reference variable (#email) gives direct access to the element, the closest match to a ref. It's rarely used for forms because reactive forms already hold the values.

Angularlogin.component.ts
@Component({  selector: "app-login",  template: `    <form (submit)="onSubmit($event, email.value, password.value)">      <label for="email">Email</label>      <input id="email" type="email" #email />​      <label for="password">Password</label>      <input id="password" type="password" #password />​      <button type="submit">Login</button>    </form>  `,})export class LoginComponent {  onSubmit(event: SubmitEvent, email: string, password: string) {    event.preventDefault(); // plain (submit), no forms module    console.log(email, password);  }}

Differences and gotchas

  • Setting ref.current.value = '' to clear a field works, but it changes the DOM behind React's back. Prefer form.reset() (Entry 5).
  • In Angular, a plain (submit) without the forms module behaves like HTML: you need preventDefault() again. With ReactiveFormsModule or FormsModule imported, use (ngSubmit).

Quick reference

ReactAngular
useRef<HTMLInputElement>(null)#email template variable / viewChild()
ref.current.valueemail.value
defaultValuevalue attribute