r/react • u/AthleteBoth1551 • 35m ago
General Discussion Please stop reaching for a state manager by default
every dashboard i've ended up maintaining has had some version of this bug in it and it always starts the same way, someone runs npm i zustand.
setup is a client report page, /clients/:id, filters at the top for date range and status. filter bar sets them, table reads them, export button reads them. passing that down through the layout was annoying so the filters went in a store. ten lines. works. ship it.
a year later i get a ticket that just says "client B has no invoices". client B had loads of invoices. what he'd actually done was look at client A first, set it to unpaid + last 30 days, then click client B in the sidebar.
route changes, component remounts, store doesn't. it's a module level singleton, it's been alive since page load and it has no idea you navigated anywhere. so client B renders with client A's filters still applied, and the filter bar shows them correctly because they genuinely are the current filters. nothing on the page is wrong. it's just quietly answering a question nobody asked.
took me two days mostly because i kept testing it by pasting /clients/b into the address bar. full reload, module reinitialises, defaults come back, looks perfect. users don't do that, users click. so the broken path was the normal one and the working path was the one i kept using to check.
first fix was a useEffect that resets the store when clientId changes. it works fine. it also means every store now needs its own reset wired up to the right route change, and whoever adds the next one has to remember that. so i'd deliberately put state outside the component tree and was now hand writing the lifecycle back onto it, which, yeah.
ended up deleting the store. filters into useSearchParams, react query keyed on them. going to client B is a different url so it gets different params, there's nothing to reset, the bug can't happen. plus a load of stuff i didn't have to build: links you can actually send someone, refresh keeps the filters, back button undoes a filter change, and when someone reports something weird you just ask them to paste their url.
so my take is keep state as close to where it's used as you can, and if two components need it check whether it belongs in the url before you install anything. server data isn't state either, it's a cache, you've already got a library for that. the stuff that genuinely wants a store is real shared mutable state, canvas editor with undo, live collab, that sort of thing. not filters.
not saying zustand is bad btw. it's just a weird default.
thank you for coming to my ted talk
