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 #26: Don't let 'Single Source of Truth' become blind dogma
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #26: Don't let 'Single Source of Truth' become blind dogma

I used to believe every variable in an application must reside in a single Store. That was a costly mistake of thousands of lines of redundant code.

TP
Truong PhamSoftware Engineer
PublishedJune 2, 2024

I used to be a fanatical follower of the slogan: "Single Source of Truth." In the early days of getting familiar with Redux, I believed that for an application to be predictable, every state—from User info to whether a dropdown is open or closed—must be centrally managed in the Global Store.

I believed it because it sounded like very "standard architecture." "If everything is in one place, I can easily debug with Redux DevTools, can Time Travel, and never fear data being out of sync." That was theory. But reality gave me a cold shower in a complex financial management Dashboard project.

1. Context: When the Store becomes a "trash heap"

The system at the time had dozens of charts and data tables. Because I believed in a Single Source of Truth, I pushed isSidebarOpen, isModalLoading, and even the inputValue of each search box into Redux.

The result? Every time a user typed a character, the entire massive Store updated, leading to 70% of unrelated components on the screen having to re-render because they were "listening" to some part of the Store. The project was so laggy that typing wasn't even smooth.

2. Concept: State Locality

The problem was that I had confused Global Truth (Data that truly needs sharing) and Ephemeral State (Temporary state that disappears when the component unmounts).

// How I used to do it (Sounds "correct" but is wrong)
const SearchComponent = () => {
  const value = useSelector(state => state.search.tempValue);
  const dispatch = useDispatch();

  return <input value={value} onChange={(e) => dispatch(updateSearch(e.target.value))} />;
};

Why did this sound right? Because it followed the "One-way data flow" rule perfectly. But in reality, forcing an input to go all the way to the Global Store before returning to display itself is a massive waste of resources.

3. Common Mistake and Balance

A Junior will also often do what I did: fearing that state will be "scattered," so they push everything to Context or Store. They consider useState something "amateur" compared to Redux.

Comparison between two ways:

  • Doing it "by the book" (dogmatically): Everything in the Store -> Easy to debug with tools but slow app, long code because of having to write Action/Reducer for even small things.
  • Doing it practically (Balanced): State sits at the closest place where it's used.
    • Dropdown open? useState at that component.
    • Product filtering data? Local state of that page.
    • Login info? Global Store.

Lesson Learned

I realized that "Single Source of Truth" does not mean "Single Object of Everything."

  • When the old way is still correct: In very small apps, or data that truly needs to be synchronized across 3-4 different screens.
  • When to avoid: Avoid pushing pure UI states (open/close, hover, input text) into the Global Store. It only makes your system heavier and harder to reuse components.

The truth doesn't lie in a single place; it sits at the most appropriate place for each component.


Notes on the day I learned to place values correctly.

Series • Part 26 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #27: useEffect is not the place for doing all synchronizing logic
Blog #25: Context API Perf Hit – When the 'standard React' solution betrays you
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 you26Blog #26: Don't let 'Single Source of Truth' become blind dogmaReading27Blog #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 cleaner31Blog #31: 1 AM and the Search for the 'Missing' File
TP

Written by Truong Pham

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

Read more articles