By a Developer Who Knows Better But Doesn't Do Better
Look, I need to come clean about something. We all do, really. Because somewhere in the back of our collective developer consciousness, we know we're doing it wrong. We know we're making choices that would make our past selves weep and our future selves curse our names. But here we are, still doing it anyway.
It started innocently enough. I was knee-deep in yet another React project, drowning in prop drilling so intense it felt like I was mining for data through seventeen layers of components. Then I discovered Zustand.
"A small, fast, and scalable bearbones state-management solution."
Bearbones. Bear. How adorable is that? It's like the developer equivalent of a comfort animal, but for state management.
I remember thinking, "This is it. This is my salvation. No more Redux boilerplate that reads like a legal document. No more Context spaghetti that would make an Italian grandmother cry. No more passing props down so many levels that I needed a GPS to find where they ended up."
But you know what they say about old habits. They don't just die hard—they fake their own deaths and come back to haunt you at 3 AM when you're trying to debug why your component tree looks like a genealogy chart from a soap opera.
The Comfort Food of Code
Despite Zustand sitting right there, being all elegant and straightforward, I still catch myself writing things like:
const ThemeContext = createContext({ theme: 'light', setTheme: ()
=> {} });
And then—because apparently I hate myself—I wrap my entire app in a
<ThemeProvider>
that just uses
Zustand under the hood anyway.
Why do I do this to myself? Because React Context is like comfort food for developers. It's the mac and cheese of state management. Sure, it's basically mashed potatoes with extra butter and gravy made from the tears of future-you trying to debug fourteen nested providers, but it feels familiar. It feels like home.
There's something perversely satisfying about opening up the React DevTools and seeing this beautiful disaster:
<ThemeProvider> <AuthProvider>
<NotificationProvider> <PermissionProvider>
<UserSettingsProvider> <LanguageProvider>
<ToastProvider> <ModalProvider> <App />
</ModalProvider> </ToastProvider>
</LanguageProvider> </UserSettingsProvider>
</PermissionProvider> </NotificationProvider>
</AuthProvider> </ThemeProvider>
It's like a Russian nesting doll of questionable life choices, and I'm oddly proud of it.
The Bear That Was Supposed to Save Me
Zustand promised me freedom. It whispered sweet promises in my ear: "Just create your store and use it anywhere. No Provider needed! No ceremony required! Just pure, clean state management!"
So I'd write something sensible like:
export const useStore = create((set) => ({ darkMode: false,
toggleDarkMode: () => set((state) => ({ darkMode: !state.darkMode
})), }));
Clean. Simple. Beautiful.
And then, two minutes later, like a recovering addict falling off the wagon:
const SettingsContext = createContext({ toggleDarkMode: () => {}
});
WHY? Why do I do this to myself? It's like adopting a perfectly well-behaved dog to get over your toxic ex, but then you keep texting your ex at 2 AM anyway. "U up? Want to manage some state together?"
The Unholy Fusion
But my absolute favorite abomination—the one that makes me question my career choices and wonder if I should have become a baker instead—is this masterpiece:
const ZustandContext = createContext(null); export const
ZustandProvider = ({ children }) => { const store = useStore(); //
FROM ZUSTAND return ( <ZustandContext.Provider value={store}>
{children} </ZustandContext.Provider> ); };
Yes. I put Zustand in Context. I took the bear and put it in a cage. Then I buried the cage in a forest of providers. Then I set fire to the forest and called it "architecture."
When junior developers ask me why I did this, I put on my most serious face and say, "It's for separation of concerns." Then I immediately log off for the day and contemplate my life choices.
The Selector Struggle
I know Zustand has selectors. I know it has subscriptions and middleware and all sorts of wonderful things that would make my life easier. But do I use them properly? Of course not.
Instead, I write gems like this:
const { value1, value2, value3 } = useMyStore((state) => ({ value1:
state.value1, value2: state.value2, value3:
state.deeply.nested.value3, }));
And then I memoize it. Because performance, right? Then I pass it into a Context Provider. Then I read it in a child component that's nested nine layers deep because "clean architecture" and "separation of concerns."
Meanwhile, my app's performance profile looks like a heart monitor during a panic attack, and I'm sitting there wondering why my bundle size is the size of a small country.
The Intervention I Need
So here's my confession: I am the problem. I abuse React Context like it owes me money. I misuse Zustand like it's a Swiss Army knife when all I needed was a butter knife. I wrap global state in so many layers of abstraction that it needs its own passport to travel between components.
I crave complexity the way some people crave chocolate. I am the Pope of Overengineering, the Archbishop of Unnecessary Abstractions, the Grand Wizard of "What If We Just Added One More Layer?"
But here's the thing—and this is important—I'm not alone in this
madness. You've done it too. Don't lie to me. You've built a
GlobalSettingsContextProviderWrapperRoot.tsx
and told yourself it was "the scalable solution." You've created
custom hooks that wrap other custom hooks that wrap Context that
manages Zustand state, and you've called it "maintainable."
We're all in this together, folks. We're all fighting the same battle against our own worst instincts.
The Path to Redemption
Maybe—just maybe—it's time to let Zustand be the bear it wants to be. Free range. No Context cage. No ceremony. No seventeen layers of abstraction between "I need this value" and "here's the value."
Maybe we can learn to trust that simple solutions can be good solutions. That not everything needs to be wrapped in a Provider. That sometimes, the straightforward approach is the right approach.
Unless... I mean, what if we just wrapped it in one teeny tiny Provider? Just for testing purposes? Just to make it easier to mock? Just to...
No. Stop. Step away from the keyboard.
The End (For Now)
This is my story. This is my confession. I am a Context abuser and a Zustand misuser, and the first step to recovery is admitting you have a problem.
If you've ever uttered the phrase "Let's abstract this Zustand store into a custom hook that wraps a Context so we can swap it later if we ever change libraries," please seek help immediately. We have support groups. We have meetings. We have coffee and we talk about our feelings and our inability to just use the tools as they were designed.
But until then, I'll be here, still wrapping my state management in unnecessary layers, still creating Context where none is needed, still telling myself that this time will be different.
Because old habits don't just die hard—they're basically immortal, and they have excellent health insurance.
P.S. If you're reading this and nodding along, congratulations—you're part of the problem too. Welcome to the club. We meet Thursdays, and we always over-engineer the meeting agenda.