Using Immer

immerPage.why.title

immerPage.why.description

// Without Immer - verbose and error-prone
set((state) => ({
  ...state,
  nested: {
    ...state.nested,
    deep: {
      ...state.nested.deep,
      value: newValue
    }
  }
}))

// With Immer - simple and intuitive
set((state) => {
  state.nested.deep.value = newValue
})

immerPage.installation.title

npm install immer
# or
yarn add immer
# or
pnpm add immer

immerPage.basicUsage.title

immerPage.basicUsage.description

import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

const useStore = create(
  immer((set) => ({
    todos: [],
    addTodo: (todo) => set((state) => {
      state.todos.push(todo)
    }),
    toggleTodo: (id) => set((state) => {
      const todo = state.todos.find((t) => t.id === id)
      if (todo) {
        todo.completed = !todo.completed
      }
    }),
    deleteTodo: (id) => set((state) => {
      const index = state.todos.findIndex((t) => t.id === id)
      if (index !== -1) {
        state.todos.splice(index, 1)
      }
    }),
  }))
)

immerPage.nestedState.title

immerPage.nestedState.description

const useStore = create(
  immer((set) => ({
    user: {
      profile: {
        name: 'John',
        settings: {
          theme: 'light',
          notifications: {
            email: true,
            push: false,
          },
        },
      },
    },
    toggleEmailNotifications: () => set((state) => {
      state.user.profile.settings.notifications.email = 
        !state.user.profile.settings.notifications.email
    }),
    setTheme: (theme) => set((state) => {
      state.user.profile.settings.theme = theme
    }),
  }))
)

immerPage.combining.title

immerPage.combining.description

import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'

const useStore = create(
  devtools(
    persist(
      immer((set) => ({
        todos: [],
        addTodo: (todo) => set((state) => {
          state.todos.push(todo)
        }),
      })),
      { name: 'todo-storage' }
    ),
    { name: 'TodoStore' }
  )
)

immerPage.typescript.title

import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface Todo {
  id: string
  text: string
  completed: boolean
}

interface TodoState {
  todos: Todo[]
  addTodo: (todo: Todo) => void
  toggleTodo: (id: string) => void
}

const useStore = create<TodoState>()(
  immer((set) => ({
    todos: [],
    addTodo: (todo) => set((state) => {
      state.todos.push(todo)
    }),
    toggleTodo: (id) => set((state) => {
      const todo = state.todos.find((t) => t.id === id)
      if (todo) {
        todo.completed = !todo.completed
      }
    }),
  }))
)