State management in a web app
Four kinds of state
Server data. Lists, items, profile. These are copies of a remote source of truth and need cache treatment: loading, refresh, expiry.
UI state. An open panel, a selected tab, typed text. Lives in the component, dies with it.
Address state. Filter, pagination, search. Its place is in the URL — so it can be shared and navigated back to.
True global state. Logged-in user, theme, language. Very few things.
The common mistake
Putting everything in one global store. The result: every screen depends on every screen, data goes stale without anyone noticing, and bugs that appear only when you reach a screen in a particular order.
When you separate, most of the global code evaporates. What remains is small and easy to understand.
What needs explicit handling
Loading and error states for every fetch, preventing stale requests that return after a newer one, and optimistic updates that know how to roll back when the server refused.
Going deeper
Define one freshness policy per data type — how long a copy is considered valid and when to refresh in the background. Without an explicit policy, every screen invents its own, and then “why isn't this updating” becomes an eternal bug.