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(ordefaultChecked), notvalue. - 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:
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.
@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. Preferform.reset()(Entry 5). - In Angular, a plain
(submit)without the forms module behaves like HTML: you needpreventDefault()again. WithReactiveFormsModuleorFormsModuleimported, use(ngSubmit).
Quick reference
| React | Angular |
|---|---|
useRef<HTMLInputElement>(null) | #email template variable / viewChild() |
ref.current.value | email.value |
defaultValue | value attribute |