【问题标题】:Reactjs and Redux update state mutable or immutable?Reactjs 和 Redux 更新状态可变还是不可变?
【发布时间】:2020-07-05 16:24:22
【问题描述】:

我对 Reactjs 和 Redux 非常陌生,任何人都可以解释什么是 reactjs/redux 中的 Mutable 和 Immutable?

我正在使用 Redux 更新状态,并使用它来控制我的 UI 布局,但我发现下面的方法之一不起作用,尽管两者也在更新对象。

我有一个商店对象,如下所示:

const initialAppState = {
          showSideBar: false,
          showSwitcher: false,
          showRightPane: false,
          menu: [
                {
                  name: "Home",
                  redirect: "/",
                },
                {
                  name: "About",
                  redirect: "/about",
                  expanded: false,
                  childs: [{
                      name: "Child one", 
                      redirect: "/Child"
                  }]
                },
            ]
        }

除此之外,我还有一个 Pane.js 和 Menu.js 来呈现我的菜单列表。

Pane.js

const LeftPane = (props) => {
    return <List>
          {links.map((o, index) => {
            return <Menus key={o.name} props={o} index={index} />
          })}
    </List>
}

Menu.js

const Menus = ({ props, index, onToggleSubMenu }) => {
   return <ListItem> ...
}

我正在尝试将菜单对象的扩展值更新为 true,但是当我使用 state.menu.map 更改值时,我的反应组件不会重新渲染?它将执行到 Pane.js,我可以从 Pane.js 道具中看到扩展 = true。但它不会执行到 Menu.js?

const AppReducer = (state = initialAppState, action) => {
    return {...state, 
          menu: [
            ...state.menu.map((m, i) => {
              if (i === action.index) {
                m.expanded = !state.menu[action.index].expanded;
                } 
                return m;
            })
          ]
        }
}

另一方面,如果我从下面的代码中更新扩展值,它会起作用。

const AppReducer = (state = initialAppState, action) => {
   state.menu[action.index] = Object.assign({}, state.menu[action.index], {
      expanded: !state.menu[action.index].expanded,
    });
    return {...state, menu: state.menu}
}

这两者有什么不同?更新状态的正确方法是什么?为什么我们应该使用 Object.assign 或 spread (...) 操作符来更新状态?我已经阅读了Redux Immutale Update Patterns,上面的工作代码更像是链接中提到的常见错误。

【问题讨论】:

    标签: reactjs redux


    【解决方案1】:

    您应该在AppReducer 中做两件事。

    1. Map 函数返回一个新数组并且不会改变原始数组,因此无需在那里进行破坏。
    2. 在您的 map 函数中,您拥有对对象 m 的引用,并且您通过将 m.expanded 更改为 !m.expanded 来改变 m。这是您实际应该返回一个新对象的地方。

    你应该写AppReducer如下。

    const AppReducer = (state = initialAppState, action) => {
      return {
        ...state,
        // No need to destruct when using map, map always returns a new array
        menu: state.menu.map((m, i) => {
          if (i === action.index) {
            // Return a new object, with all properties copied, and expanded's value toggled
            return {
              ...m,
              expanded: !m.expanded;
            }
          }
          // Return the original object because no change has been made
          return m;
        }),
      };
    };
    
    

    关于扩展运算符和Object.assign的区别,根据object-rest-spread-proposal,一个是另一个的语法糖,即{...a}是写Object.assign({}, a);的更简单的方法

    【讨论】:

    • 应该是:expanded: !m.expanded 您的代码有效,但 state.menu[action.index] 已经可以作为 m 使用
    • 我的坏 HMR,感谢您的更新。我会更新答案。
    猜你喜欢
    • 2019-01-10
    • 2018-11-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 1970-01-01
    • 2021-04-26
    • 2016-06-11
    • 1970-01-01
    相关资源
    最近更新 更多