如何组织我的嵌套对象以实现更好的状态管理?

How to organise my nested object for better state management?

这更像是一个组织问题,而不是技术问题。我想我可能会增加复杂性,而更有经验的开发人员会简化它。我缺乏那种经验,需要帮助。

它是一个菜单编辑器,我在其中将我的数据库中的菜单对象加载到状态中:

state = {
  user_token: ####,
  loadingMenu: true,
  menu: {} // menu will be fetched into here
}

对象看起来像这样:

{
  menuID: _605c7e1f54bb42972e420619,
  brandingImg: "",
  specials: "2 for 1 drinks",
  langs: ["en", "es"],
  items: [
    {
      id: 0,
      type: "menuitem",
      isVisible: true,
      en: {
        name: "sandwich 1",
        desc: "Chicken sandwish"
      },
      es: {
        name: "torta 1"
      }, 
      price: 10
    },
  // ...
  // ABOUT 25 MORE ITEMS
  ]
}

UI 允许用户单击并单独更新项目。因此,当他们更改文本时,我发现自己不得不进行奇怪的解构,如下所示:

function reducer(state, action) {
  if (action.type === UPDATE_NAME) {    
    const newMenuItems = state.menu.items.map((oldItem) => {
      if (oldItem.id === action.payload.id) {
        return { ...oldItem, ["en"]: { ...oldItem["en"], name: action.payload.newName } }
        // ["en"] for now, but will be dynamic later
      }
      return oldItem
    })

    return { ...state, menu: { ...state.menu, items: newMenuItems } }

  }
}

这似乎是个坏主意,因为我要用我的新对象替换整个状态。请问有没有更好的组织方式?

我知道有不变性管理器,我尝试使用immer.js,但运行成了障碍。我需要映射我所有的菜单项以找到一个用户想要编辑(将 ID 与事件目标的 ID 匹配)。我不知道还有什么方法可以直接定位它,也不知道该怎么做:

draft.menu.items[????][lang].name = "Sandwich One"

所以,我再次认为我的组织是错误的,因为不变性管理器可能应该使这变得容易。任何想法,我可以重构什么?

首先,您当前的减速器看起来不错。那个“奇怪的解构”是 very typical。您将 总是 将整个 state 替换为一个新对象,但您处理的是浅拷贝,因此它不是每个级别的全新对象。您没有修改的菜单项仍然是对相同对象的引用。


I need to map through all my menu items to find the one user wants to edit (matching the ID to the event target's ID). I don't know how else to target it directly.

您可以使用 .findIndex() 获取要更新的项目的索引。

const {lang, name, id} = action.payload;
const index = draft.menu.items.findIndex( item => item.id === id);
if ( index ) { // because there could be no match
  draft.menu.items[index][lang].name = name;
}

This is more of an organisation than technical question. I think I may be adding complexity, where a more experienced dev would simplify. I lack that experience, and need help.

我对状态结构的建议是将所有项目存储在由 id 键入的字典中。这使得更新项目更容易,因为您不再需要在数组中查找它。

const {lang, name, id} = action.payload;
draft.items[index][lang].name = name;

menu 对象只有一个 ID 数组,而不是 items 属性 的对象数组。当您 select 一个菜单时,您的 select 或者可以用它们的对象替换 ID。

const selectMenu = (state) => {
  const menu = state.menu;
  return { ...menu, items: menu.items.map((id) => state.items[id]) };
};