DEV Community

Alex Spinov
Alex Spinov

Posted on

Zustand Has a Free API That Makes React State Management Absurdly Simple

Zustand is the minimalist state manager for React. No providers, no boilerplate, no context — just a hook and a store.

Create a Store in 5 Lines

import { create } from "zustand";

interface Store {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
}

const useStore = create<Store>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// Use it — no Provider needed!
function Counter() {
  const count = useStore((s) => s.count);
  const increment = useStore((s) => s.increment);
  return <button onClick={increment}>{count}</button>;
}
Enter fullscreen mode Exit fullscreen mode

Slices Pattern: Scalable Stores

const createScraperSlice = (set) => ({
  scrapers: [],
  activeScraper: null,
  addScraper: (scraper) => set((s) => ({ scrapers: [...s.scrapers, scraper] })),
  setActive: (id) => set({ activeScraper: id }),
});

const createDataSlice = (set) => ({
  results: [],
  loading: false,
  fetchResults: async (scraperId) => {
    set({ loading: true });
    const data = await fetch(`/api/results/${scraperId}`).then(r => r.json());
    set({ results: data, loading: false });
  },
});

const useStore = create((...a) => ({
  ...createScraperSlice(...a),
  ...createDataSlice(...a),
}));
Enter fullscreen mode Exit fullscreen mode

Middleware: Persist, DevTools, Immer

import { create } from "zustand";
import { persist, devtools } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

const useStore = create(
  devtools(
    persist(
      immer((set) => ({
        items: [],
        addItem: (item) => set((state) => {
          state.items.push(item); // Immer — mutate freely!
        }),
        removeItem: (id) => set((state) => {
          state.items = state.items.filter(i => i.id !== id);
        }),
      })),
      { name: "scraper-store" } // localStorage key
    )
  )
);
Enter fullscreen mode Exit fullscreen mode

Subscribe Outside React

// Use store anywhere — not just components
const unsubscribe = useStore.subscribe(
  (state) => state.results,
  (results) => {
    console.log("Results updated:", results.length);
    analytics.track("results_changed", { count: results.length });
  }
);

// Get state imperatively
const currentState = useStore.getState();
useStore.setState({ loading: true });
Enter fullscreen mode Exit fullscreen mode

Async Actions

const useStore = create((set, get) => ({
  data: null,
  error: null,
  loading: false,
  fetchData: async (url) => {
    if (get().loading) return; // Prevent double-fetch
    set({ loading: true, error: null });
    try {
      const res = await fetch(url);
      const data = await res.json();
      set({ data, loading: false });
    } catch (error) {
      set({ error: error.message, loading: false });
    }
  },
}));
Enter fullscreen mode Exit fullscreen mode

Selector Optimization

import { useShallow } from "zustand/react/shallow";

// Only re-render when these specific fields change
const { items, loading } = useStore(
  useShallow((s) => ({ items: s.items, loading: s.loading }))
);
Enter fullscreen mode Exit fullscreen mode

Managing scraped data state? My Apify tools deliver data ready for your Zustand stores.

Custom solution? Email spinov001@gmail.com

Top comments (0)