LogoTRUONG PHAM
Home
Projects
Portfolio
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Portfolio
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back/50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES
Blog #29: When 'Controlled Component' turns into a performance burden

Blog #29: When 'Controlled Component' turns into a performance burden

I used to believe every input in React must be a Controlled Component. Until I had to build a giant form with 100 fields.

June 15, 2024·3 min read

I admit I was once a "Control Freak" when writing React. I always believed the "standard React way" was for every input value to be stored in State and passed back to the value attribute of the Input.

I believed it because every tutorial on the internet taught it that way. "Controlled components give you absolute control over data, easy to validate, easy to format (like automatic capitalization)." It sounded perfect. But reality taught me a painful lesson when I participated in building an ERP system with "endless" Forms.

1. Context: When typing is an ordeal

The project had insurance registration forms with nearly 100 fields: Text, Number, Date, Select, Checkbox... Because I believed in the "standard," I wrapped the whole form in a large state object.

The result: Every time the User typed 1 character in the name input, the entire 100-field form re-rendered. On weak office computers, typing was delayed by 2-3 seconds. The User went crazy, and I was stuck.

2. Concept: Controlled vs Uncontrolled

The problem was I forced React to do something that the browser (plain HTML) does ten thousand times better: Managing input state.

// How I used to do it (Sounds "correct" but laggy)
const MyInput = () => {
  const [val, setVal] = useState("");
  return <input value={val} onChange={(e) => setVal(e.target.value)} />;
};

Why is it bad at large scale? Because every keystroke triggers a cycle: Keystroke -> State changes -> Re-render entire Component -> Update DOM. With 100 inputs reacting simultaneously, the browser's Main thread will be choked.

3. Practical Approach and Balance

A Junior will choose Controlled for everything because it's the first thing learned. They fear ref and consider Uncontrolled something "non-React."

Comparison of two ways:

  • The "Library Standard" way: Fully Controlled. Convenient for complex validation logic but a performance disaster for large forms. Clunky code due to too many onChange.
  • The Practical way (Uncontrolled / Hybrid):
    • Let the browser manage input values itself (using useRef or FormData).
    • Only use Controlled for fields that truly need an instant reaction (e.g., suggested search).
    • Use libraries like React Hook Form—where they use Uncontrolled to optimize re-renders but still provide an easy-to-use API.

Lesson Learned

I learned that: Loyalty to a theory is not as important as the user's actual experience.

  • When the old (Controlled) way is still correct: Small forms (< 10 fields), suggestive search boxes, inputs that need constant formatting (like money with commas).
  • When to avoid: Avoid using Controlled for large data forms, Dashboard pages with hundreds of input boxes.

Sometimes, the way to make React run fastest is... don't force it to manage what the browser already does too well.


Notes on the day I learned to let go of control to find smoothness.

Previous: Blog #28: The naivety of believing setState is an immediate assignmentAll posts in this seriesNext: Blog #30: A giant State Object doesn't make your code cleaner