React: From Components to Production · Sharing State and Structuring Apps

Client State Libraries: Zustand and Redux Toolkit

When to reach for a store and how modern stores look.

Most apps need little global client state once server state lives in TanStack Query. For the rest (UI state shared across distant components, complex client workflows), use a store: Zustand is tiny and hook-based with selectors; Redux Toolkit provides structured slices, devtools and middleware for large teams. Both let components subscribe to slices, re-rendering only when those values change.

import { create } from "zustand";

type CartState = {
  items: Record<string, number>;
  add: (sku: string) => void;
  remove: (sku: string) => void;
};

export const useCart = create<CartState>()((set) => ({
  items: {},
  add: (sku) => set((s) => ({ items: { ...s.items, [sku]: (s.items[sku] ?? 0) + 1 } })),
  remove: (sku) => set((s) => {
    const { [sku]: _removed, ...rest } = s.items;
    return { items: rest };
  }),
}));

// Select only what you need → minimal re-renders
const count = useCart((s) => Object.values(s.items).reduce((a, b) => a + b, 0));
const add = useCart((s) => s.add);

Common pitfalls

  • Putting server data in a global store and hand-writing caching.
  • Selecting the whole store object, re-rendering on any change.
  • Global state for things that could be local or in the URL.

Key points

  • Server state ≠ client state
  • Zustand: small, selector-based
  • Redux Toolkit: structure for large apps

Further reading: Zustand documentation

Practice questions

Why do selectors matter in Zustand or Redux?

Answer

Components subscribe only to the selected slice, so they re-render only when that slice changes, not on every store update.

What state usually doesn't belong in a global store?

Answer

Server data (use a query cache), form state (local or a form library), and URL-representable state like filters (use search params).

Open in the app to mark lessons complete, take notes and track your progress.