All writing
React

Optimizing React Forms - Beyond useState

Learn how to improve form performance in React by moving beyond useState

useState is usually the first hook anyone learns, and it’s easy to reach for it everywhere. That habit costs you re-renders. Here’s a login form as an example.

The Problem with useState

Every keystroke re-renders the whole component. The typical pattern:

const LoginForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    // Handle login
    console.log(email, password);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Login</button>
    </form>
  );
};

If you’re not validating on every keystroke, you don’t need to track the value on every keystroke. Read it on submit instead.

Solution 1: Using useRef

useRef holds a reference to the input without triggering re-renders:

const LoginForm = () => {
  const emailRef = useRef();
  const passwordRef = useRef();

  const handleSubmit = (e) => {
    e.preventDefault();
    // Handle login
    console.log(emailRef.current.value, passwordRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" ref={emailRef} />
      <input type="password" ref={passwordRef} />
      <button type="submit">Login</button>
    </form>
  );
};

Solution 2: Using FormData API

You often don’t need useRef either. If the inputs live inside a <form>, FormData covers it:

const LoginForm = () => {
  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);

    // Handle login
    console.log({
      email: formData.get("email"),
      password: formData.get("password"),
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" />
      <input type="password" name="password" />
      <button type="submit">Login</button>
    </form>
  );
};

When useState still makes sense

Reach for useState when you actually need the value during typing:

  1. Real-time validation
  2. Immediate feedback based on input
  3. The input value drives other UI
  4. Controlled components syncing with external state

Performance impact

Re-render counts for a form with 5 inputs:

ApproachRe-renders per keystrokeRe-renders for 100 characters
useState5500
useRef00
FormData00

Conclusion

useState isn’t wrong, it’s just overused for forms. If you don’t need the value mid-keystroke, useRef or FormData gives you the same result with zero re-renders and less code.

Before adding useState to a form input, check whether you actually need the value before submit. Usually you don’t.

0 claps
If this was useful, let me know.
Getting Started with Create Expo Stack React Native Performance Tips - Real-world Examples