You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux Toolkit中使用Map/Set是否属于不良实践?

Redux状态中使用Map/Set是否为不良实践?

问题背景

我正在使用Redux Toolkit构建Redux Store,希望在状态中存储用户映射,代码实现如下:

export type UserType = {
  _id: string
  firstName: string; 
  lastName: string;
  email: string;
};

interface UserStoreType {
  users: Map<string, UserType> 
}

const initialState: UserStoreType = {
  users: new Map();
};

export const userSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
      append: (state, action) => {
        const user = action.payload;
        state.users.set(user._id, user);
      }
  },
});

export const userAction = userSlice.actions;
export const userReducer = userSlice.reducer;

但我看到有观点称在Redux状态中使用Map或Set是反模式,应避免。请问这种用法是否属于不良实践?若是,推荐方案是什么?是否应改用普通JavaScript对象替代Map?

我知道Redux Toolkit允许在reducer中安全修改状态,但不确定Map/Set的使用是否存在问题或不被推荐。我选择Map是因为认为其按键查找性能更优,且API便于遍历键/值,原本预期这是存储键值对的合理方式,但网上讨论似乎不推荐,希望明确是否应避免使用Map/Set,或该场景下是否可行。


回答

1. 是否属于不良实践?

严格来说不是绝对的反模式,但确实不推荐在Redux状态中使用Map/Set,核心原因在于Redux的设计基础:

  • Redux要求状态是可序列化的纯JS对象/数组,Map/Set无法被JSON.stringify这类默认工具正确处理,会导致状态持久化(比如localStorage缓存)、Redux DevTools状态回溯、服务端渲染的序列化环节出错。
  • 虽然Redux Toolkit的Immer支持直接修改Map/Set,但这只是技术上可行,并不符合社区主流的最佳实践。

2. 推荐方案:改用普通JS对象存储

对于这种键值对存储场景,直接用以用户_id为键的普通JS对象是最优解,改造后的代码如下:

export type UserType = {
  _id: string
  firstName: string; 
  lastName: string;
  email: string;
};

// 改为对象类型,键为用户_id,值为UserType
interface UserStoreType {
  users: Record<string, UserType> 
}

const initialState: UserStoreType = {
  users: {}; // 初始化为空对象
};

export const userSlice = createSlice({
  name: "users", // 原代码的"counter"是笔误,建议改为对应业务名称
  initialState,
  reducers: {
      append: (state, action) => {
        const user = action.payload;
        // Immer会自动处理不可变更新,直接赋值即可
        state.users[user._id] = user;
      },
      // 额外示例:删除用户
      remove: (state, action) => {
        const userId = action.payload;
        delete state.users[userId];
      }
  },
});

export const userAction = userSlice.actions;
export const userReducer = userSlice.reducer;

3. 性能与遍历的替代方案

你担心的Map的优势,用普通对象完全可以覆盖:

  • 按键查找:对象属性访问state.users[userId]的性能和Map的get方法几乎无差异,日常业务场景下完全够用。
  • 遍历操作:可以用Object.keys()、Object.values()、Object.entries()实现,比如:
    // 获取所有用户数组
    const allUsers = Object.values(userStore.users);
    // 遍历键值对
    Object.entries(userStore.users).forEach(([userId, user]) => {
      // 处理逻辑
    });
    

4. 特殊场景的例外

如果你的业务确实依赖Map的独有特性(比如非字符串类型的键、严格的键顺序、超大规模数据的频繁增删),可以考虑使用,但需要自己处理序列化问题:比如把Map转换为Array.from(map.entries())存储,读取时再转成Map。但这种方式会增加代码复杂度,非必要不建议。


内容的提问来源于stack exchange,提问作者Hashi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 16:43:29