πŸ—οΈ
Architecture

State Management: Redux vs Zustand vs Context 🧠

Compare different state management solutions and learn when to use each one in your React Native projects. No more confusion!

Lisa WangLisa Wang
January 5, 2024
15 min read
Architecture
State Management
Redux
Zustand
Context

State Management: Redux vs Zustand vs Context 🧠

Choosing the right state management solution can make or break your React Native app. Let's compare the most popular options and learn when to use each one!

React Context

Perfect for simple, localized state:

import React, { createContext, useContext, useReducer } from 'react';

const AppContext = createContext();

const appReducer = (state, action) => {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'SET_LOADING':
      return { ...state, loading: action.payload };
    default:
      return state;
  }
};

export const AppProvider = ({ children }) => {
  const [state, dispatch] = useReducer(appReducer, {
    user: null,
    loading: false,
  });

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
};

export const useApp = () => useContext(AppContext);

Redux Toolkit

For complex, predictable state management:

import { createSlice, configureStore } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    profile: null,
    loading: false,
    error: null,
  },
  reducers: {
    setLoading: (state, action) => {
      state.loading = action.payload;
    },
    setUser: (state, action) => {
      state.profile = action.payload;
      state.loading = false;
    },
    setError: (state, action) => {
      state.error = action.payload;
      state.loading = false;
    },
  },
});

export const { setLoading, setUser, setError } = userSlice.actions;

export const store = configureStore({
  reducer: {
    user: userSlice.reducer,
  },
});

Zustand

The sweet spot between simplicity and power:

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useUserStore = create(
  persist(
    (set, get) => ({
      user: null,
      loading: false,
      
      setUser: (user) => set({ user }),
      setLoading: (loading) => set({ loading }),
      
      login: async (credentials) => {
        set({ loading: true });
        try {
          const user = await authAPI.login(credentials);
          set({ user, loading: false });
        } catch (error) {
          set({ loading: false });
          throw error;
        }
      },
      
      logout: () => set({ user: null }),
    }),
    {
      name: 'user-storage',
    }
  )
);

When to Use What?

Use Context When:

  • Simple, localized state
  • Theme or language preferences
  • Small to medium apps
  • Avoiding prop drilling

Use Redux When:

  • Complex state logic
  • Time-travel debugging needed
  • Large team development
  • Predictable state updates

Use Zustand When:

  • Want simplicity with power
  • TypeScript-first approach
  • Minimal boilerplate
  • Modern React patterns

Performance Considerations

// Optimize Context with multiple providers
const UserProvider = ({ children }) => (
  <UserContext.Provider value={userValue}>
    {children}
  </UserContext.Provider>
);

const ThemeProvider = ({ children }) => (
  <ThemeContext.Provider value={themeValue}>
    {children}
  </ThemeContext.Provider>
);

// Use selectors in Zustand
const userName = useUserStore((state) => state.user?.name);
const userEmail = useUserStore((state) => state.user?.email);

Choose wisely, and your state will be in perfect harmony! 🧠

Was this page helpful?

Help us improve our documentation