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 #25: Context API Perf Hit – When the 'standard React' solution betrays you
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #25: Context API Perf Hit – When the 'standard React' solution betrays you

Analyzing the decision to replace React Context with a specialized state management library for a complex Admin Panel system.

TP
Truong PhamSoftware Engineer
PublishedMay 30, 2024

The project was a large-scale Admin Panel system with a team of 5 people. The system managed thousands of configurations for marketing campaigns. We decided to use the React Context API to manage all settings (Settings, Theme, User Permissions) because it's a "standard solution" built into React, requiring no extra libraries.

The Problem: When a simple click freezes the whole website

When the system was small, everything was smooth. But as the component tree grew to hundreds or thousands of nodes, a problem appeared. Every time a user changed a small design setting (e.g., changing the main dashboard color), the entire massive admin tree re-rendered from top to bottom.

The core issue with Context API is: It doesn't have a Selector mechanism. If you change a themeColor in a large Context object, all components using useContext(ConfigContext) will re-render, even if they only care about userPermissions and don't care about colors.

Options Considered

We was caught between two choices:

Option 1: Split Contexts

  • Solution: Instead of a single AppProvider, we split it into ThemeProvider, UserProvider, NotificationProvider,...
  • Pros: Still uses pure React Hooks. Reduces the radius of re-render impact.
  • Cons: When states are related, managing the pile of Providers (Wrapper Hell) becomes a nightmare. The code looks very messy.

Option 2: Switch to Zustand (External State Manager)

  • Solution: Use an external state management library based on the Pub/Sub model.
  • Pros: Has an extremely powerful Selector mechanism. Only components that truly need revised data will re-render. Performance is nearly absolute.
  • Cons: Depends on an external library (though Zustand is only about 1KB). Mindset shift from "React Context" to "Store".

Final Decision and Analysis

After using the React Profiler to see the "red zone" of re-renders spreading across the site, I decided to choose Option 2.

// Example of the difference that helps optimize performance
// This component ONLY re-renders when themeColor actually changes
const color = useConfigStore(state => state.themeColor); 

// Whereas with the old Context:
// The entire component re-renders no matter what changes in config!
// const { themeColor } = useContext(ConfigContext); 

Impact on Performance: "Scripting" time in the Chrome Performance Tab dropped from 1.2 seconds to 150ms for each settings change. The user experience became instantly smooth.

Impact on Maintainability: The codebase became cleaner. We no longer had to stack dozens of Providers on top of each other in the App.tsx file. Logic was encapsulated into small Stores, making it easy to test and expand.

Impact on Team: Developers loved the simplicity of Zustand. They no longer worried about accidentally causing "re-render earthquakes" whenever they added a new global variable.

Self-Reflection: Was it Over-engineering?

I wonder: Could splitting Contexts (Option 1) have solved the problem? Possibly, but it's like using a screwdriver to hammer a nail. Context API was created for passing data afar (dependency injection), not for managing complex, constantly changing states.

If I went back to that time, would I choose differently? Perhaps I wouldn't have chosen Context at the beginning for a large Admin system. The biggest lesson is: Don't sacrifice performance and system stability for the sake of "purity" (zero-dependency).

Lesson Learned

Choose the right tool for the right problem. Context API is great for data that rarely changes (Locale, Current User). But for dynamic and complex data, use solutions designed specifically for that purpose. Don't cling to the "standard React" label if it's hurting your project.


Notes on the journey of giving up purity to find true performance.

Series • Part 25 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #26: Don't let 'Single Source of Truth' become blind dogma
Blog #24: Failed Optimistic Update – When efforts to 'smooth' UI turn into UX disasters
20Blog #20: The Mystery of LCP – Why do 'fast' websites still get rated low?21Blog #21: The Global State Nightmare – The decision to 'topple' the Redux monument22Blog #22: Click Race Condition – When User's click speed beats your App23Blog #23: Multi-tab Syncing – When your application 'talks' between tabs24Blog #24: Failed Optimistic Update – When efforts to 'smooth' UI turn into UX disasters25Blog #25: Context API Perf Hit – When the 'standard React' solution betrays youReading26Blog #26: Don't let 'Single Source of Truth' become blind dogma27Blog #27: useEffect is not the place for doing all synchronizing logic28Blog #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 cleaner
TP

Written by Truong Pham

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

Read more articles