При создании нового виджета-Reactjs, Redux, была обнаружена мутация состояния - PullRequest
0 голосов
/ 24 мая 2018

Ошибка при создании списка виджетов

если я впервые щелкаю по списку виджетов, я могу создать виджет (виджет 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,
},
});

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Одна распространенная ошибка, которая привела к этой ошибке для меня, была, когда я клонировал в состояние из реквизита.

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;
    }
  );
}
0 голосов
/ 24 мая 2018

Редактировать: Ошибки в действии создателя

Попробуйте использовать 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.addWidget actionCreator?

У меня есть идея относительно того, что может пойти не так.Оператор распространения является мелким клоном, поэтому вы не можете быть неизменным.(Ищите варианты 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 };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...