Skip to main content

Learning Objectives

By the end of this module, you’ll understand:
  • When to choose Redux Toolkit vs. Zustand (and why Zustand often wins for mobile)
  • Redux Toolkit setup with typed slices
  • Zustand for lightweight, performant state
  • State persistence patterns for mobile apps
  • Common pitfalls with global state on React Native

Choosing a State Library

The React Native ecosystem offers many global state libraries, but two dominate production apps: Redux Toolkit and Zustand. Think of them as a pickup truck vs. a sports car — both get you there, but they are optimized for different jobs. The practical recommendation: Start with Zustand for new React Native projects. Its tiny footprint matters on mobile, and its API is simpler to onboard teammates with. Reach for Redux Toolkit when you need its middleware ecosystem (RTK Query, listener middleware) or when your team already has Redux expertise.

Redux Toolkit

Installation

Store Configuration

Typed Slice Example

Typed Hooks

Mobile pitfall with Redux: Every dispatched action triggers the root reducer, and useSelector runs on every store update. In a busy mobile app with real-time data, this can cause performance issues. Always use specific selectors that return the narrowest slice of state possible, and use createSelector from RTK for memoized derived data.

Zustand

Installation

Basic Store

Zustand stores are plain functions — no providers, no reducers, no action types. You call create, define your state and actions in one place, and consume it with a hook.

Real-World Store with Async Actions

Selective Subscriptions (Performance)

One of Zustand’s biggest advantages on mobile is that components only re-render when the specific value they subscribe to changes.
Mobile tip: For MMKV-based persistence (faster than AsyncStorage by 30x), replace createJSONStorage(() => AsyncStorage) with a custom MMKV storage adapter. This makes a noticeable difference on app startup when restoring large persisted stores.

Best Practices

  1. One store per domain, not one giant store — Create separate Zustand stores for auth, projects, UI preferences, etc. This keeps each store focused and prevents unrelated re-renders.
  2. Never put server data in global state — Use React Query for data from your API. Global stores are for client state (UI preferences, auth tokens, local-only data).
  3. Persist selectively — Only persist what the user expects to survive an app restart. Transient state like loading flags and error messages should not be persisted.
  4. Use the functional updater — Always use set((state) => ...) instead of set({ ... }) when the new state depends on the previous state. This avoids race conditions from rapid state updates.