A mid-level developer understands that not all state should be global. Knowing where data lives is the first step to a clean architecture.
1The Immutability Law
Never mutate state directly. Using patterns like the 'spread operator' or libraries like 'Immer' ensures that your framework's change detection works perfectly every time.
2Atomic State
Libraries like Recoil or Jotai use 'Atoms'βtiny pieces of state that components can subscribe to individually. This prevents the 'Mega-Store' bottleneck where every update re-renders the whole app.
3Asynchronous Sagas
Handling side effects (like API calls) is the hardest part of state management. Whether you use Thunks, Sagas, or Observables, the goal is to keep your logic separate from your UI.
4Step-by-Step Breakdown
State is the 'Brain' of your application. Managing it incorrectly leads to the most common (and hardest to find) bugs in frontend development.
Prop Drilling occurs when you pass data through 5 levels of components just to reach one child. Global state solutions solve this by providing a 'Teleport' for your data.
Redux isn't 'too much code' anymore. Redux Toolkit (RTK) has modernized the pattern with 'Slices' and 'Thunks', making complex state predictable and testable.
Sometimes you don't need a heavy engine. Zustand offers a hook-based global state that is zero-boilerplate and highly performant for medium-sized apps.
What is the primary role of a 'Reducer' in the Redux pattern?
- βTo reduce the size of the final JavaScript bundle
- βTo take the current state and an action, and return a NEW state object
- βTo fetch data from an external API
- βTo render the UI on the screen
What does the 'Single Source of Truth' principle imply?
- βEvery developer should use the same code editor
- βThe entire state of your application should be stored in one central object (or store)
- βYou should only have one CSS file for the whole site
- βOnly the senior developer is allowed to merge code
Level Up π
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Announcing State Changes
When state updates change what's visible on screen (a cart total, a saved indicator, a validation error), sighted users see it instantly but screen reader users get no signal unless the change lands in an aria-live region.
<div aria-live="polite">{itemCount} items in cart</div>SEO Implications
- 1
Client-Only State Hides Content From Crawlers
State that only populates after a useEffect or a client-side fetch renders empty on the server. If that state drives primary page content, search engines may index a blank shell instead of the real content β hydrate critical data server-side or in the initial render.
Best Practices
Colocate State With Its Consumers
Don't lift state to a global store by default. If only one component tree reads and writes a value, keep it local β global state adds indirection and re-render surface area you don't need.
Normalize Relational Data
Store collections as { id: entity } maps instead of nested arrays. It turns O(n) lookups and updates into O(1) and avoids deep-cloning entire trees just to change one field.
Frequent Bugs
A component reads state inside a callback (like a setTimeout or event handler) and gets a 'stale' value from the render it was created in, even though the state has since updated.
Use the functional updater form (setCount(c => c + 1)) instead of referencing the outer state variable directly, or add the value to the effect/callback's dependency array.
Real-World Examples
Shopping Cart Store
An e-commerce app needs cart contents accessible from the header badge, the cart drawer, and the checkout page, with changes in any of them reflected everywhere instantly.
const useCartStore = create((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })),
}));