【问题标题】:A state mutation was detected between dispatches while creating new widget-Reactjs ,Redux在创建新的小部件-Reactjs,Redux 时,在调度之间检测到状态突变
【发布时间】:2018-11-03 18:12:23
【问题描述】:

创建小部件列表时出错

如果我第一次点击小部件列表,我可以创建小部件(小部件 1)。

如果我为其创建另一个小部件,我会收到错误(状态突变)。

如果我创建另一个小部件,我可以创建没有错误的小部件(小部件 3)

错误:在路径 `` 中检测到调度之间的状态突变。这可能会导致错误的行为。

代码

addWidget = (widgetType) => {
let newWidget = {};
switch (widgetType) {
  case 'venn':
    newWidget = {
      widgetName: `widget ${this.state.widgetCount}`,
      widgetType: 'venn',
      leftTarget: '',
      rightTarget: '',
      leftTargetValue: 0,
      rightTargetValue: 0,
      position: { row: 0, col: 0 },
    };
    break;
default:
    newWidget = {
      widgetName: `Default ${this.state.widgetCount}`,
      widgetType: 'venn',
      leftTarget: '',
      rightTarget: '',
    };
    break;
}
this.setState(prevState => ({
  widgetsList: [...this.state.widgetsList, newWidget],
  isHidden: !prevState.isHidden,
  widgetCount: this.state.widgetCount + 1,
}), () => this.props.storyboardActions.addWidget(newWidget, this.props.selectedBoard.boardId));  
};

减速器

case types.ADD_NEW_WIDGET: {
  const newBoardList = state.boardList.map((item) => {
    if (item.boardId === action.selectedBoardId) {
      return {
        ...item,
        modifiedAt: Date.now(),
        widgetList: [...(item.widgetList || []), { widgetId: uuid(), ...action.newWidget }],
      };
    }
    return item;
  });
  return { ...state, boardList: [...newBoardList] };
}

动作

export const addWidget = (newWidget, selectedBoardId) => ({
type: types.ADD_NEW_WIDGET,
payload: { 
widget: newWidget,
boardId: selectedBoardId,
},
});

【问题讨论】:

    标签: reactjs redux


    【解决方案1】:

    编辑:动作创建者中的错误

    尝试使用 action.payload 传递您的所有数据。您目前的属性名称存在错误,并且您正在以错误的顺序调用带有参数的动作创建者:

    // Action Creator
    export const addWidget = (newWidget, selectedBoardId) => ({  // <-- Param order
      type: types.ADD_NEW_WIDGET,
      payload: {  // <-- Use payload object
        boardId: selectedBoardId,  // <-- These key names
        widget: newWidget,
      }
    });
    
    
    // Reducer
    case types.ADD_NEW_WIDGET: {
      const { boardId, widget } = action.payload;  // <-- Get the data out
      const newBoardList = state.boardList.map(item => {
        if (item.boardId === boardId) {
          const currentWidgetList = item.widgetList || [];
          const newWidgetList = [
            ...currentWidgetList,
            {
              widgetId: uuid(),
              ...widget
            }
          ];
    
          return {
            ...item,
            modifiedAt: Date.now(),
            widgetList: newWidgetList
          };
        }
    
        return item;
      });
      return { ...state, boardList: newBoardList };
    }
    

    我可以看看你的storyboardActions.addWidgetactionCreator 吗?

    我知道可能出了什么问题。扩展运算符是一个浅克隆,因此不能保证您是不可变的。 (查找 cloneDeep 以获取选项。)

    不过,我不肯定什么是变异。我对您发布的代码的最佳想法是widgetName,因为它依赖this.state.widgetCount 来获取唯一名称,但setState 方法是异步的,所以从技术上讲它有可能出错(我认为.)

    我对您的代码进行了一些重构。我没有改变太多,所以我不指望这个解决问题。但是像这样分开这些位会让你有机会在 console.logs 中折腾,希望能抓住突变发生的位置。

    如果这样做有什么不同,请告诉我:

    getNewWidget = type => {
      switch (widgetType) {
        case "venn":
          return {
            widgetName: `widget ${this.state.widgetCount}`,
            widgetType: "venn",
            leftTarget: "",
            rightTarget: "",
            leftTargetValue: 0,
            rightTargetValue: 0,
            position: { row: 0, col: 0 }
          };
        default:
          return {
            widgetName: `Default ${this.state.widgetCount}`,
            widgetType: "venn",
            leftTarget: "",
            rightTarget: ""
          };
      }
    };
    
    addWidget = widgetType => {
      const newWidget = this.getNewWidget(widgetType);
      this.setState(
        {
          widgetsList: [...this.state.widgetsList, newWidget],
          isHidden: !this.state.isHidden,
          widgetCount: this.state.widgetCount + 1
        },
        () =>
          this.props.storyboardActions.addWidget(
            newWidget,
            this.props.selectedBoard.boardId
          )
      );
    };
    

    减速器

    case types.ADD_NEW_WIDGET: {
      const newBoardList = state.boardList.map(item => {
        if (item.boardId === action.selectedBoardId) {
          const currentWidgetList = item.widgetList || [];
          const newWidgetList = [
            ...currentWidgetList,
            {
              widgetId: uuid(),
              ...action.newWidget
            }
          ];
    
          return {
            ...item,
            modifiedAt: Date.now(),
            widgetList: newWidgetList
          };
        }
    
        return item;
      });
      return { ...state, boardList: newBoardList };
    }
    

    【讨论】:

    • 如果我没记错,组件数据将是相同的,只是动作和reducer发生了变化
    • 您需要在组件中调度 updateWidget 操作,并检查减速器上的 UPDATE_WIDGET 类型。获取排列好的名称和参数顺序。
    • const { boardId, widget } = action.payload; boardId 未定义
    • 查看你调用它的组件。 this.props.storyboardActions.updateWidget( newWidget, this.props.selectedBoard.boardId )this.props.selectedBoard.boardId 需要定义。
    • 仍然面临同样的问题
    【解决方案2】:

    对我来说导致此错误的一个常见错误是,当我从道具克隆到状态时。

    getFields(props) {
      return ((props.resources.fields && [...props.resources.fields]) || []).map(
        field => {
          field.checked = false;
          return field;
        }
      );
    }
    
    this.setState({ fields: getFields(props) });
    

    在上面的例子中,fields 是一个对象数组。而不是做一个深拷贝,我用[...props.resources.fields]做了一个浅拷贝。

    做一个深拷贝可以解决这个问题。所以,就我而言,我做了一层浅拷贝,如下所示:

    getFields(props) {
      return ((props.resources.fields && [...props.resources.fields]) || []).map(
        item => {
          const field = { ...item };
          field.checked = false;
          return field;
        }
      );
    }
    

    【讨论】:

      猜你喜欢
      • 2019-12-25
      • 2017-07-17
      • 2019-10-13
      • 2017-11-04
      • 1970-01-01
      • 2016-12-08
      • 2017-04-24
      • 2021-10-20
      • 1970-01-01
      相关资源
      最近更新 更多