# Selectors and subscriptions

A selector picks the state a component needs; a subscription connects that component, or another listener, to future store updates.

Your store is a hook: create it with state and actions, then select what each component needs. A snapshot read answers “what is the state now?” without subscribing. A hook subscription keeps the selected value current, and selector-output equality determines whether a store update causes a React re-render.

## How updates reach a component

[`create`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#create) returns a hook with store methods attached. Calling its `getState()` method reads the current state without registering a listener. Calling the hook in a component subscribes through React and returns the selector's output.

```mermaid
flowchart TD
  A["Action calls set()"] --> B["Store updates state and notifies listeners"]
  B --> C["React reads the selector output"]
  C --> D["Compare output with Object.is"]
  D --> E["Different: re-render with the new output"]
  D --> F["Equal: no store-driven re-render"]
  B --> G["Explicit subscribe() listener runs"]
  H["getState()"] --> I["Current snapshot, no subscription"]
```

React compares computed selector outputs with `Object.is`. Selecting a number or an existing action function works without constructing a wrapper. Calling the hook without a selector returns the entire state, so every change to that state can cause a re-render.

## Compare a snapshot with a subscription

Use this as `src/App.tsx` in your React application. It follows the starter's separate selectors for state and actions. The screen shows a live count, a count captured at mount, and an increment button.

```tsx title="src/App.tsx"
import { useEffect, useState } from 'react'
import { create } from 'zustand'

type CounterStore = {
  count: number
  inc: () => void
}

const useCounterStore = create<CounterStore>()((set) => ({
  count: 0,
  inc: () => set((state) => ({ count: state.count + 1 })),
}))

export default function App() {
  const count = useCounterStore((state) => state.count)
  const inc = useCounterStore((state) => state.inc)
  const [countAtMount] = useState(() => useCounterStore.getState().count)

  useEffect(() => {
    const unsubscribe = useCounterStore.subscribe((state, previousState) => {
      console.log('Count changed:', previousState.count, '→', state.count)
    })
    return unsubscribe
  }, [])

  return (
    <main>
      <p>Live count: {count}</p>
      <p>Count at mount: {countAtMount}</p>
      <button onClick={inc}>Increment</button>
    </main>
  )
}
```

Click **Increment**. The live count changes; the count captured at mount stays unchanged. Each later `getState()` call reads the latest state, but the number already captured does not update itself.

The explicit `subscribe()` listener runs synchronously when the store changes and receives both the new and previous full state. It returns an unsubscribe function, which the effect uses as cleanup on unmount. Registering this listener does not itself request a React render; the count selector provides that connection in this sample.

## Keep computed outputs stable

[`useShallow`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-react-shallow#useshallow) returns a selector that reuses its previous output reference when the new output is shallowly equal. This lets you construct an object or array from several state picks without treating an equivalent wrapper as a new value.

Install React explicitly if your project does not already include it; it is an optional peer dependency of Zustand:

```bash
npm install zustand react react-dom
```

The bear-meals example illustrates the distinction: changing a meal changes the store, but not the list of bear names. Replace `src/App.tsx` with this complete example. It renders the names, papa bear's meal, and a button to change that meal.

Here, [`createStore`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#createstore) creates a vanilla store and [`useStore`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#usestore) connects its selected values to React.

```tsx title="src/App.tsx"
import { createStore, useStore } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

type BearMeals = {
  papaBear: string
  mamaBear: string
  littleBear: string
}

const mealsStore = createStore<BearMeals>()(() => ({
  papaBear: 'large porridge-pot',
  mamaBear: 'middle-size porridge pot',
  littleBear: 'A little, small, wee pot',
}))

function BearNames() {
  const names = useStore(mealsStore, useShallow((state) => Object.keys(state)))
  return <p>Bear names: {names.join(', ')}</p>
}

function MealControls() {
  const meal = useStore(mealsStore, (state) => state.papaBear)

  return (
    <section>
      <p>Papa bear's meal: {meal}</p>
      <button
        onClick={() => mealsStore.setState({ papaBear: 'a large pizza' })}
      >
        Order pizza
      </button>
    </section>
  )
}

export default function App() {
  return (
    <main>
      <BearNames />
      <MealControls />
    </main>
  )
}
```

Click **Order pizza**. The meal text changes to `a large pizza`; the names remain `papaBear, mamaBear, littleBear`. Although `Object.keys()` constructs a new array, its entries are unchanged. `useShallow` returns the previous array reference, so this meal update does not cause a store-driven re-render of `BearNames`.

Shallow comparison checks top-level values, not nested contents recursively. It does not stop re-renders caused by a component's parent or its own React state.

### Avoid fresh references in v5 selectors

In v5, a selector that returns a fresh object, array, or fallback function on every read can cause an infinite update loop. Keep the **output** reference stable: use separate selectors for individual values, wrap shallowly equal computed outputs in `useShallow`, or declare a fallback function outside the component rather than creating it inside the selector. This applies both to bound hooks from `create` and to `useStore`.

For custom equality functions and further rendering techniques, see [Prevent unnecessary rerenders](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/prevent-unnecessary-rerenders).

## The API involved

- `getState()` returns the current state without subscribing. Use it in event handlers or other imperative code when you need a fresh snapshot.
- The hook returned by `create` selects and subscribes in React without a provider. With a vanilla store from `createStore`, use `useStore(store, selector)` for that connection instead.
- `subscribe(listener)` listens to full-state changes and returns an unsubscribe function. It does not call the listener at registration.
- [`subscribeWithSelector`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#subscribewithselector) adds `subscribe(selector, listener, options)` for imperative listeners interested in a slice. The listener receives the new and previous selected values. Its `equalityFn` defaults to `Object.is`; `fireImmediately` optionally calls the listener at registration with the current selected value as both arguments. See [Subscribe outside React](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/subscribe-outside-react) for setup and cleanup.
- `useShallow(selector)` preserves a shallowly equal output reference so React's output comparison can recognize it as unchanged.

Try the [live Zustand demo](https://zustand-demo.pmnd.rs/) to see a store-backed counter, or continue with [State and actions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/state-and-actions) for updating the state your selectors read.
