• 简体中文
  • Zustand 中的 Map 与 Set

    Map 和 Set 是可变的数据结构。要在 Zustand 中使用它们,你必须在更新时创建一个新实例。

    Map

    读取 Map

    const foo = useSomeStore((state) => state.foo)

    更新 Map

    务必创建一个新的 Map 实例:

    // Update single entry
    set((state) => ({
      foo: new Map(state.foo).set(key, value),
    }))
    
    // Delete entry
    set((state) => {
      const next = new Map(state.foo)
      next.delete(key)
      return { foo: next }
    })
    
    // Update multiple entries
    set((state) => {
      const next = new Map(state.foo)
      next.set('key1', 'value1')
      next.set('key2', 'value2')
      return { foo: next }
    })
    
    // Clear
    set({ foo: new Map() })

    Set

    读取 Set

    const bar = useSomeStore((state) => state.bar)

    更新 Set

    务必创建一个新的 Set 实例:

    // Add item
    set((state) => ({
      bar: new Set(state.bar).add(item),
    }))
    
    // Delete item
    set((state) => {
      const next = new Set(state.bar)
      next.delete(item)
      return { bar: next }
    })
    
    // Toggle item
    set((state) => {
      const next = new Set(state.bar)
      next.has(item) ? next.delete(item) : next.add(item)
      return { bar: next }
    })
    
    // Clear
    set({ bar: new Set() })

    为什么要使用新实例?

    Zustand 通过比较引用来检测变化。直接修改 Map 或 Set 不会改变它的引用:

    // ❌ Wrong - same reference, no re-render
    set((state) => {
      state.foo.set(key, value)
      return { foo: state.foo }
    })
    
    // ✅ Correct - new reference, triggers re-render
    set((state) => ({
      foo: new Map(state.foo).set(key, value),
    }))

    陷阱:空集合的类型提示

    在初始化空的 Map 和 Set 时,请提供类型提示:

    {
      ids: new Set([] as string[]),
      users: new Map([] as [string, User][])
    }

    如果不提供类型提示,TypeScript 会推断为 never[],导致之后无法添加元素。

    演示

    基础示例:https://stackblitz.com/edit/vitejs-vite-5cu5ddvx