mediumReact Native#12

Zustand store with persist

Prompt

Create a Zustand store for a simple counter with increment and decrement actions. Persist the state to AsyncStorage using zustand/middleware.

Solution

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import AsyncStorage from '@react-native-async-storage/async-storage'

const useStore = create(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((s) => ({ count: s.count + 1 })),
      decrement: () => set((s) => ({ count: s.count - 1 })),
    }),
    { name: 'counter-storage', storage: createJSONStorage(() => AsyncStorage) }
  )
)
Mentor's take

Zustand's pitch is state outside the React tree: the store is a plain closure, components subscribe via the hook, and — the part worth saying in an interview — selectors like useStore(s => s.count) mean a component re-renders only when its selected slice changes, not on every store write. No Provider, no context cascade, actions colocated with state.

The persistence layering is the RN-specific knowledge: persist serializes state on change and rehydrates on startup, and createJSONStorage(() => AsyncStorage) adapts AsyncStorage's promise-based, string-only API to the middleware. Because AsyncStorage is async, rehydration is not instantaneous — the store starts at count: 0 and the persisted value arrives a tick later. Production code gates on onRehydrateStorage / hasHydrated to avoid flashing defaults or overwriting saved state. Also reach for partialize to persist only what should survive a restart — persisting everything is how transient UI state gets fossilized.

The security boundary matters most: AsyncStorage is plaintext. Persisting a counter is fine; persisting tokens or PII through this middleware ships secrets to unencrypted storage.

Red flag: persisting auth tokens via zustand-persist-to-AsyncStorage. Sensitivity decides the storage tier — secrets go to Keychain/Keystore-backed storage, never a persisted store.

Say it: "Zustand keeps state outside the tree with selector-scoped re-renders; persist plus a JSON storage adapter handles AsyncStorage — but rehydration is async and the store is plaintext, so I gate on hydration and never persist secrets."