Skip to main content

Core Concepts

Overview​

Zustand Q builds on the foundation of Zustand, enhancing it with features tailored for modern React applications. This section explores the core concepts that define zustand-q, including its differences from vanilla Zustand, support for asynchronous operations, and integrations like devtools and persistence.

Differences from Zustand's create​

Zustand Q’s createStore extends the original create function from Zustand, offering a more powerful and type-safe API. Here’s how it differs:

  • Asynchronous Support:

    • create: Limited to synchronous state updates via set.
    • createStore: Adds built-in queries and mutations for async data fetching and updates.
  • Simplified Devtools & Persistence:

    • create: Requires manual middleware setup (e.g., devtools, persist).
    • createStore: Integrates these features directly with devtoolsName and persistName options.
  • Type Improvements:

    • create: Basic TypeScript support with manual type definitions.
    • createStore: Enhanced typing for state, actions, queries, and mutations using generics.

Example Comparison​

Vanilla Zustand (create):

import create from "zustand";

interface State {
count: number;
}

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

Zustand Q (createStore):

import { createStore } from "zustand-q";

interface State {
count: number;
}

const useStore = createStore<State, { increment: () => void }>({
initialData: { count: 0 },
actions: (set) => ({
increment: () => set((state) => ({ count: state.count + 1 })),
}),
devtoolsName: "CounterStore",
});

In zustand-q, the configuration object provides a structured approach, with optional async features and better TypeScript inference.

Queries and Mutations​

Zustand Q introduces two powerful abstractions for managing asynchronous state:

Queries​

Queries handle data fetching and store updates, inspired by Tanstack React Query’s useQuery.

  • Purpose: Fetch data from an API and sync it with the store.

  • Key Features:

    • queryFn: Async function to fetch data.
    • onStore: Updates the store with fetched data.
    • enabled: Controls automatic fetching (default: true).
    • Lifecycle hooks: onStart, onSuccess, onError, onFinish.
  • Example:

    const useStore = createStore({
    initialData: { items: [] as string[] },
    queries: {
    getItems: {
    queryFn: async () => ({ data: ["item1", "item2"] }),
    onStore: (data, set) => set({ items: data.data }),
    },
    },
    });

    const { items, getItems } = useStore();
    getItems({ enabled: true });

Mutations​

Mutations manage state changes like creating, updating, or deleting data.

  • Purpose: Perform async operations that modify the store.

  • Key Features:

    • mutationFn: Async function to execute the mutation.
    • onStore: Applies the mutation result to the store.
    • Lifecycle hooks: onStart, onSuccess, onError, onFinish.
  • Example:

    const useStore = createStore({
    initialData: { items: [] as string[] },
    mutations: {
    addItem: {
    mutationFn: async (vars: { name: string }) => ({ data: vars.name }),
    onStore: (data, set) =>
    set((state) => ({ items: [...state.items, data.data] })),
    },
    },
    });

    const { addItem } = useStore();
    const { mutate } = addItem();
    mutate({ name: "newItem" });

For detailed usage, see createStore, Queries, and Mutations.

Devtools and Persistence​

Zustand Q simplifies debugging and state persistence with built-in options:

Devtools​

Enable Redux DevTools integration for real-time state inspection.

  • Usage:

    const useStore = createStore({
    initialData: { count: 0 },
    actions: (set) => ({
    increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    devtoolsName: "MyStore",
    });
  • Result: Open Redux DevTools in your browser to see the store labeled "MyStore", track state changes, and replay actions.

  • Benefits: Streamlines debugging without extra middleware setup.

Persistence​

Persist state to local storage to retain it across sessions.

  • Usage:

    const useStore = createStore({
    initialData: { theme: "light" as "light" | "dark" },
    persistName: "app-theme",
    });
  • Result: The state is saved to localStorage under "app-theme" and restored on page reload.

  • Benefits: Ideal for preserving user preferences or app settings with minimal effort.

Key Benefits​

  • Unified API: Combines synchronous and asynchronous state management in one configuration.
  • Type Safety: Leverages TypeScript generics for robust error checking (see Type Improvements).
  • Ease of Use: Reduces boilerplate for devtools and persistence compared to vanilla Zustand.
  • Flexibility: Scales from simple state to complex async workflows.

Zustand Q’s core concepts make it a modern, developer-friendly evolution of Zustand, tailored for real-world React applications.