Back to roadmaps zustand Course

Updating Nested State and Arrays

In React and Zustand, state updates must be immutable. You must never mutate the existing state directly. Instead, you must return a shallow copy of the state containing the updated properties.


1. Updating Array Lists

To modify arrays in your store, use standard JavaScript immutable methods like the spread operator ..., .map(), or .filter().

A. Appending an Item

addTodo: (newText) => set((state) => ({
  todos: [...state.todos, { id: Date.now(), text: newText, done: false }]
}))

B. Removing an Item

removeTodo: (todoId) => set((state) => ({
  todos: state.todos.filter((todo) => todo.id !== todoId)
}))

C. Modifying an Item Properties

toggleTodo: (todoId) => set((state) => ({
  todos: state.todos.map((todo) =>
    todo.id === todoId ? { ...todo, done: !todo.done } : todo
  )
}))

2. Modifying Nested Objects

Updating deeply nested objects manually using spread operators can lead to long, hard-to-read lines:

// Messy nested state copy
updateAddress: (newCity) => set((state) => ({
  user: {
    ...state.user,
    profile: {
      ...state.user.profile,
      address: {
        ...state.user.profile.address,
        city: newCity
      }
    }
  }
}))

3. Simplifying Updates using Immer

To avoid nesting copy hell, you can integrate the Immer library. Immer allows you to write mutation-like code inside a draft block, and it compiles that code into safe immutable updates automatically.

First, install the immer utility package:

npm install immer

Next, import and wrap your state updates inside the immer helper:

import { create } from "zustand";
import { immer } from "zustand/middleware/immer";

interface UserStore {
  user: {
    name: string;
    profile: {
      city: string;
    };
  };
  updateCity: (city: string) => void;
}

export const useUserStore = create<UserStore>()(
  immer((set) => ({
    user: {
      name: "John",
      profile: {
        city: "Boston",
      },
    },
    
    // Write mutation-like updates safely on the draft parameter
    updateCity: (city) =>
      set((state) => {
        state.user.profile.city = city;
      }),
  }))
);
Published on Last updated: