Skip to main content

Selectors

Overview

Selectors in Zustand Q allow you to efficiently access and derive specific parts of the state from a store. They are a core feature of Zustand, enabling you to extract values or compute derived state while minimizing unnecessary re-renders. By using selectors, you can optimize performance and keep your components reactive only to the state they care about.

Syntax

Selectors can be used in two ways when accessing state from a createStore:

1. Function Selector

const value = useStore((state) => state.key);

2. String Selector

const value = useStore<T>("key");
  • useStore: The hook provided by your store (e.g., useMyStore) or zustand-q’s global useStore.
  • state => state.key: A function selector that takes the entire state and returns a specific value or computed result.
  • "key": A string selector that directly accesses a state property by its key.
  • T: The TypeScript generic type for the selected value, ensuring type safety (required for string selectors).

Note: Both selector types are memoized by Zustand, meaning the component only re-renders when the selected value changes (based on shallow equality). In zustand-q, string selectors are a convenient shorthand for function selectors.

Usage

Basic Example

Access a counter value from a store:

import { createStore } from "zustand-q";

interface State {
count: number;
}

export const useMyStore = createStore({
initialData: { count: 0 } as State,
});

const App = () => {
const count = useMyStore((state) => state.count); // Function selector
const count2 = useMyStore<number>("count"); // String selector

return (
<div>
Count: {count} (or {count2})
</div>
);
};

Computed Values with Function Selector

Derive a computed value:

import { createStore } from "zustand-q";

interface State {
firstName: string;
lastName: string;
}

export const useMyStore = createStore({
initialData: { firstName: "John", lastName: "Doe" } as State,
});

const App = () => {
const fullName = useMyStore(
(state) => `${state.firstName} ${state.lastName}`
);

return <div>Full Name: {fullName}</div>;
};

Optimizing Performance

Prevent unnecessary re-renders with selectors:

import { createStore } from "zustand-q";

interface State {
count: number;
unrelatedData: string;
}

export const useMyStore = createStore({
initialData: { count: 0, unrelatedData: "hello" } as State,
actions: (set) => ({
increment: () => set((state) => ({ count: state.count + 1 })),
updateUnrelated: () => set({ unrelatedData: "world" }),
}),
});

const Counter = () => {
const count = useMyStore((state) => state.count); // Only re-renders when count changes
console.log("Counter rendered");
return <div>Count: {count}</div>;
};

const App = () => {
const { increment, updateUnrelated } = useMyStore();
return (
<div>
<Counter />
<button onClick={increment}>Increment</button>
<button onClick={updateUnrelated}>Update Unrelated</button>
</div>
);
};

In this example, the Counter component only re-renders when count changes, not when unrelatedData is updated, thanks to memoization.

Comparison

FeatureString SelectorFunction Selector
SyntaxSimple ("key")Flexible (fn)
Type SafetyYes (via T)Yes (inferred)
Computed ValuesNoYes
MemoizationYes (automatic)Yes (automatic)
Use CaseDirect accessDerived state