Selectors
A selector is a function that extracts and derives a specific piece of data from the store. They are critical for preventing unnecessary re-renders.
Without selectors - everything re-renders
tsx
// component re-renders on ANY store change
const store = useStore();
const username = store.user.name;With selectors - targeted subscriptions
tsx
// re-renders ONLY when user.name changes
const username = useStore(s => s.user.name);
// re-renders ONLY when cart length changes
const cartCount = useStore(s => s.cart.items.length);Derived selectors with memoization
ts
import { create } from 'zustand';
import { createSelector } from 'reselect'; // or write manually
const selectCart = (s: Store) => s.cart.items;
const selectDiscount = (s: Store) => s.cart.discount;
// reselect - only recalculates when inputs change
const selectTotal = createSelector(
[selectCart, selectDiscount],
(items, discount) => {
const subtotal = items.reduce((sum, item) => sum + item.price, 0);
return subtotal * (1 - discount);
}
);
const total = useStore(selectTotal);Zustand - built-in shallow comparison
tsx
import { useShallow } from 'zustand/react/shallow';
// subscribes to multiple values without re-rendering if references match
const { name, email } = useStore(
useShallow(s => ({ name: s.user.name, email: s.user.email }))
);Redux - reselect
ts
import { createSelector } from '@reduxjs/toolkit';
export const selectCompletedTodos = createSelector(
[(s: RootState) => s.todos],
(todos) => todos.filter(t => t.completed)
);
// result is memoized - list recalculates only when todos changesPoorly written selectors that return new object/array references on every call (
s => s.items.filter(...)) will cause components to re-render on every store update, even when nothing relevant changed.
updated
did you know it?