【问题标题】:Redux A state mutation was detected before reach the reducerRedux 在到达 reducer 之前检测到状态突变
【发布时间】:2017-01-26 23:40:39
【问题描述】:

我从视图发送到操作 this.props.appClasses 这是视图:

 <div key={key} className='SDWanAppClassTreeItem' onClick={this.props.actions.handleToggleAppClass.bind(this,this.props.appClasses, 0, key)}>

在我修改从视图中获得的 appClasses 的操作中,我想将修改后的 appClasses 发送到 reducer 以更新 appClasses 状态。但它在到达减速器之前给了我一个错误。

检测到状态突变 这是行动:

export function handleToggleAppClass(appClasses, parentAppClassId, appClassId) {
  // console.log('handleToggleAppClass', appClassId, this.appClasses[appClassId]);

  if (appClass.parentAppClassId == 0) {
    // Flip the appClass Show Property
    appClasses[appClass.appClassId].show = appClasses[appClass.appClassId].show ? false : true;

    if (Object.keys(appClasses[appClass.appClassId].children).length !== 0) {
      // Regardless if we enable or disabled the parent, all children should be disabled
      for (var childKey in appClasses[appClass.appClassId].children) {
        appClasses[appClass.appClassId].children[childKey].show = false;
      }
    }
  } else {
    // If we are enabling a child, make sure parent is disabled
    if (!appClasses[appClass.parentAppClassId].children[appClass.appClassId].show) {
      appClasses[appClass.parentAppClassId].show = false;
    }
    appClasses[appClass.parentAppClassId].children[appClass.appClassId].show = appClasses[appClass.parentAppClassId].children[appClass.appClassId].show ? false : true;
  }

  dispatch(handleUpdateInitialSourceFetch(appClasses));

  return { type: types.TOGGLE_APP_CLASS, appClasses };
}

如您所见,我确实想修改 action 上的 appClasses,因为我必须使用新修改的 appClasses 来更新另一个 reducer 上的另一个状态。在这里,我想修改 appClasses 状态(在一个 reducer 中)并通过在函数调用 onother action dispatch(handleUpdateInitialSourceFetch(appClasses)) 中调度修改后的 appClasses; 我想修改另一个状态 selection.appClassesIds(在另一个处理用户选择的减速器中):

const selection = {
  timespan: "-3660",
  direction: 0,
  appClassIds: []
};


export function updateAppClassesIds(appClasses) {
  var appClassIds = [];
  for (var key in appClasses) {
    if (appClasses[key].show) appClassIds.push(key);
    if (Object.keys(appClasses[key].children).length !== 0) {
      // Regardless if we enable or disabled the parent, all children should be disabled
      for (var childKey in appClasses[key].children) {
        if (appClasses[key].children[childKey].show) appClassIds.push(childKey);
      }
    }
  }

  return { type: types.UPDATE_APP_CLASSES_IDS, appClassIds };
}

如何将 appClasses 变量从视图发送到操作而不会出现该错误。 或者我应该将 selection 和 appClasses 状态放在一个大状态中,以便能够访问这两个状态,因为它们相互依赖?

【问题讨论】:

  • appClasses 是否已经处于状态?即,您是否直接改变状态?
  • Yes 是我从 this.props.appClasses 得到的状态
  • 你不能直接改变状态;这是 Redux 的主要理念之一。通常,这种转换不会通过在此处设置整个数组来表示,而是通过传递一个 ID 及其新状态来表示。在 reducer 中,您将复制旧状态并在新的应用程序类状态中拼接。
  • 我知道,但是我需要那个变量来发送给动作,它可以是副本或类似的东西,我从帖子中读到有人做了类似 const a = [... this.props.appClasses] 但没有用。如何发送该变量的值?
  • (出于格式原因将评论移至答案。)

标签: javascript reactjs redux react-redux


【解决方案1】:

您可以通过创建一个接收要修改的 appClass 的 ID 和新值 (true/false) 的操作来实现此目的。

然后你的 reducer 应该能够处理你的状态更新,然后通过订阅的组件传播。

【讨论】:

  • (这或多或少是我在评论中所说的,但省略了评论中讨论切片/等的部分,这是 OP 遇到的困难。)
  • 我希望 appClasses 在操作中可访问,以使用 dispatch(handleUpdateInitialSourceFetch(appClasses)); 更新另一个依赖于 appClasses 的操作中的另一个状态;我应该将 appClasses 和 selection.appClassIds 组合在一个减速器中吗?因为它们相互依赖。我可以发送 appClass 的 id,但是正如您在函数中看到的那样,有很多逻辑我可以将所有这些逻辑放在减速器中吗?
【解决方案2】:

我的意思是,“切换应用程序类”的适当负载不是应用程序类的完整列表,而是更接近于:

{ 
    type: types.TOGGLE_APP_CLASS, 
    payload: appClass.appClassId
}

状态修改应该在reducer中完成——明确的what reducers are for

reducer 是一个纯函数,它接受前一个状态和一个动作,并返回下一个状态。

您正在尝试 (a) 直接在动作创建器中修改状态,以及 (b) 尝试在错误的位置修改状态。一旦你开始在正确的地方做事,解决这个问题就会容易得多。

在你的 reducer 中,你要么slice(如果这是一个数组)要么做一个对象克隆来更新单个切换并将状态设置为这个新的状态副本。但是你直接改变状态;这样做会破坏 Redux 带来的很多东西(例如时间旅行)。

【讨论】:

  • 如您所见,我正在尝试在那里修改 appClasses,因为我想用它来更新另一个需要 appClasses 修改发送 dispatch(handleUpdateInitialSourceFetch(appClasses)) 的状态“selection.appClassIds”;也许我认为我必须在一个减速器中的一个大状态上拥有 selection.appClassIds 和 appClassesId。并且请注意修改很复杂我认为只有动作应该处理修改变量的代码量,然后将修改后的 var 发送到 reducer 并更新状态。我用完整的代码更新了我的问题。
  • 我正在将应用程序从 altjs 转换为 redux,所以我很迷茫,我可以对减速器中的状态进行所有这些更改吗?
猜你喜欢
  • 1970-01-01
  • 2019-12-25
  • 1970-01-01
  • 2017-07-17
  • 2017-11-04
  • 2019-10-13
  • 2023-03-23
  • 2017-04-24
  • 2020-06-19
相关资源
最近更新 更多