LogoTRUONG PHAM
Home
Projects
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
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Blog #33: 10 PM and the 'Infinite Loop' of Punishment
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #33: 10 PM and the 'Infinite Loop' of Punishment

When striving for 'abstraction' with Custom Hooks turns into an inescapable re-render trap.

TP
Truong PhamSoftware Engineer
PublishedJune 30, 2024

End of the Sprint, pressure to complete the real-time data sync feature. I created a Custom Hook called useEverything (its real name was useAppSync) with the intention: "Just call this Hook, and everything from Auth, Sockets, and Data will be taken care of."

Everything looked very professional until the website started freezing. The browser reported "RangeError: Maximum call stack size exceeded." An infinite re-render loop. I spent 3 hours checking every useEffect, every useCallback. I started to get irritable and wondered if React was betraying me.

1. What happened?

An infinite re-render loop was created because a single Custom Hook integrated too much interdependent logic. Premature abstraction hid the data flow, making debugging extremely difficult.

2. What I thought the cause was

I assumed it was because the server was returning socket data too fast (spamming). I tried using throttle for the socket, but the issue persisted.

3. How I debugged it

I used React Profiler to determine which component was rendering the most. Then, I started "commenting out" each piece of logic in the useAppSync Hook until the loop stopped.

4. The moment of realization

My "all-powerful" Hook was observing too many things. Just a small change in userToken would trigger the socket, the socket updated data, data changed userToken through an underlying middleware... A death spiral. I had overused Custom Hooks to hide complexity, but in reality, I was just concentrating bombs in one place.

5. The fix

  • Quick patch: Add isInitial flag variables and use useRef to block redundant re-renders for the night.
  • Real fix: Scrap that giant Hook. Break it down into useAuth, useSocket, and separate data hooks. Accept writing a few extra lines of code in the Component in exchange for clarity in the data flow.

6. Looking back

Abstraction is a tonic, but an overdose turns it into poison. Don't try to create "Black Boxes" too early without a clear understanding of the data flow. If I were to redesign, I would prioritize clarity (explicitness) over conciseness.

Series • Part 33 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #34: My Machine Runs Smooth, the User's Machine Smokes
Blog #32: Friday Afternoon, 800 Lines of Code, and a Miscalculation
28Blog #28: The naivety of believing setState is an immediate assignment29Blog #29: When 'Controlled Component' turns into a performance burden30Blog #30: A giant State Object doesn't make your code cleaner31Blog #31: 1 AM and the Search for the 'Missing' File32Blog #32: Friday Afternoon, 800 Lines of Code, and a Miscalculation33Blog #33: 10 PM and the 'Infinite Loop' of PunishmentReading34Blog #34: My Machine Runs Smooth, the User's Machine Smokes35Blog #35: Clean Architecture – The Dream and the Harsh Reality36Blog #36: Touching Legacy Code – When a refactor effort becomes a disaster37Blog #37: The Reusability Trap – When a 'multi-purpose' Component becomes a burden38Blog #38: Micro-frontend Reality Check – When theoretical glory meets harsh reality
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles