DEV Community

Cover image for React: Optimizing Forms with Controlled and Uncontrolled Components
Reme Le Hane
Reme Le Hane

Posted on • Originally published at remejuan.substack.com

React: Optimizing Forms with Controlled and Uncontrolled Components

React forms can get complex quickly, especially as they grow in fields and complexity. Knowing when to use controlled and uncontrolled components effectively can simplify form management.

Trick: Combining Controlled and Uncontrolled Components for Form Optimization

Controlled Components in React are inputs that are fully controlled by React state (via useState), meaning every keystroke triggers a re-render. This is great for real-time validation but can become inefficient with large forms.

Uncontrolled Components use refs to access DOM values directly without requiring state, making them faster and ideal for inputs you don’t need to validate live.

Here’s a mix of both approaches to make an optimized form that’s still easy to manage.

Example: A Mixed-Controlled Form

Let’s create a form where some fields use controlled components and others use uncontrolled components.

import React, { useState, useRef } from "react";

const OptimizedForm = () => {
  // Controlled input for real-time validation or tracking

  const [username, setUsername] = useState("");

  const [error, setError] = useState("");

  // Uncontrolled inputs for fields that don't need live validation
  const emailRef = useRef();
  const passwordRef = useRef();

  const handleUsernameChange = (e) => {
    const value = e.target.value;

    setUsername(value);

    // Example validation
    if (value.length < 3) {
      setError("Username must be at least 3 characters.");
    } else {
      setError("");
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    // Accessing values of uncontrolled inputs
    const email = emailRef.current.value;
    const password = passwordRef.current.value;

    // Example form submission or validation logic
    console.log("Username:", username);
    console.log("Email:", email);
    console.log("Password:", password);
  };

  return (

    <form onSubmit={handleSubmit}>
      <div>
        <label>Username (Controlled):</label>
        <input
          type="text"
          value={username}
          onChange={handleUsernameChange}
        />
        {error && <p style={{ color: "red" }}>{error}</p>}
      </div>

      <div>
        <label>Email (Uncontrolled):</label>
        <input type="email" ref={emailRef} />
      </div>

      <div>
        <label>Password (Uncontrolled):</label>
        <input type="password" ref={passwordRef} />
      </div>
      <button type="submit">Submit</button>
    </form>

  );

};

export default OptimizedForm;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Controlled Component (Username):
  • We use useState to manage the username, enabling real-time validation (checking the character count).

  • This is ideal for fields that require live updates or validation.

  1. Uncontrolled Components (Email and Password):
  • We use useRef to get values directly from the DOM without managing them in state.

  • This is useful for fields like email or password where live validation might not be necessary.

  1. Form Submission:
  • On form submit, we access the values of both controlled and uncontrolled inputs seamlessly.

Why This Trick is Useful

  • Performance: Reduces re-renders by avoiding state updates for every keystroke in large forms.

  • Simplicity: Keeps form logic cleaner and more maintainable by separating fields that need validation from those that don’t.

Mastering this technique will help you balance performance and responsiveness in React forms, especially in apps with complex or large forms!

Top comments (2)

Collapse
 
brense profile image
Rense Bakker

You don't need refs either, you can access the form data directly from the forms onSubmit or onChange event.

Collapse
 
eastcoast8264 profile image
Mark M