Неперехваченное инвариантное нарушение: между отправками обнаружена мутация состояния,

Я пытаюсь отобразить только сегмент массива запросов, который я сопоставил из состояния с реквизитами моего компонента контейнера (используя mapStateToProps).

Для этого я использую оператор распространения Array.slice, а затем перебираю подмассив для создания компонентов. В моем приложении я могу успешно отображать подмассив и страницу через сегменты полного массива, но всякий раз, когда я запускаю действие для перетасовки или замены элементов в массиве, я всегда получаю эту ошибку после отправки соответствующего действия в свой редукторы:

Неперехваченное инвариантное нарушение: между отправками> обнаружена мутация состояния в пути querys.0.submit_time._isValid. Это может привести к неправильному поведению.

Я получаю эту ошибку только при первом нажатии. Эта ошибка также никогда не возникает, если я отображаю весь массив. В этом массиве каждому элементу (запросу) соответствует момент 1. Вышеупомянутая ошибка возникает в первом элементе, который не отображается в приложении (в этом случае запрос 0 был пропущен. Если я отображал запросы 0–9, то ошибка была бы для запроса 10). Поскольку submit_time предположительно мутируется, я использовал moment.clone () для его глубокого копирования, но все равно получаю ошибку. Кто-нибудь знает, как отобразить фрагмент массива и избежать этой ошибки?


РЕДАКТИРОВАТЬ:

Вот упрощенный код моего компонента, действия и редуктора. Моему компоненту сначала передаются запросы из хранилища redux в качестве опоры. Затем он передает часть этого массива дочернему компоненту, который создает tr для каждого элемента (посредством итерации). В приложении вы можете запустить обработчик событий onSortChange, включив переключатель. Это запускает действие SORT_QUERIES_BY_FIELD, которое заставляет queryReducer сортировать массив с помощью List.sort. Ошибка возникает, когда я запускаю действие onSortChange в первый раз и прямо перед отправкой действия SORT_QUERIES_BY_FIELD:

Составная часть:

    import React, {PropTypes} from 'react';
    import {connect} from 'react-redux';
    import {bindActionCreators} from 'redux';
    import moment from 'moment';
    import * as queryActions from '../../actions/queryActions';
    import QueryList from './QueryList';
    import RadioGroup from 'react-radio';

    class TroubleshootPage extends React.Component {
      constructor(props, context) {
        super(props, context);
        this.onSortChange = this.onSortChange.bind(this);
        this.state = {querys: [...this.props.querys]};
      }
      componentWillReceiveProps(nextProps) {
        this.setState({querys: nextProps.querys});
      }
      onSortChange(value, event) {
        this.props.actions.sortQueriesByField(value);
      }

      render() {
        const {querys, radioGroupValue} = this.state;
        let subList = [...querys].slice(0,9);
        return (
          <div>
           <RadioGroup name="sorts" defaultValue={null} onChange={this.onSortChange.bind(this)}>
             <input type="radio" value="total_elapsed_time" />Elapsed Time
           </RadioGroup>
            <QueryList selectedQuery={this.selectedQuery}
                       selectedQueryStep={this.selectedQueryStep}
                       querys={subList} />
          </div>
        );
      }
    }
    TroubleshootPage.propTypes = {
      querys: PropTypes.array.isRequired,
      actions: PropTypes.object.isRequired
    };
    function mapStateToProps(state, ownProps) {
      return {querys: state.querys};
    }
    function mapDispatchToProps(dispatch) {
      return {actions: bindActionCreators(queryActions, dispatch)};
    }
    export default connect(mapStateToProps, mapDispatchToProp)(TroubleshootPage);

Действие:

    import * as types from './actionTypes';
    import queryApi from '../api/mockQueryApi';

    export function sortQueriesByField(field) {
      return { type: types.SORT_QUERIES_BY_FIELD, field};
    }

Редуктор:

    import * as types from '../actions/actionTypes';
    import initialState from './initialState';

    export default function queryReducer(state = initialState.querys, action) {
      switch(action.type) {
        case types.SORT_QUERIES_BY_FIELD:
          let {field} = action;
          var querys = [...state];
          querys.sort(function(a,b) {return a[field]-b[field]})
          return querys;

        default:
          return state;
      }
    }

person C. Hidalgo    schedule 03.08.2016    source источник
comment
Пожалуйста, опубликуйте код ваших редукторов, действий, mapStateToProps ...   -  person Majky    schedule 03.08.2016


Ответы (2)


У меня была та же проблема, но, наконец, она заработала, когда я создал переменную вне своего класса и обновил как переменную, так и состояние. Вот код. Это было очень трогательно, и я знаю, что это антипаттерн, но он работает!

import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import { Row, Col } from 'react-bootstrap';
import {bindActionCreators} from 'redux';
import DatePicker from 'react-datepicker';
import * as actions from '../actions/checkoutActions';
import moment from 'moment';

import 'react-datepicker/dist/react-datepicker.css';

let date = moment();

class AppointmentPage extends React.Component{
  constructor(props, context){
    super(props, context);
    this.createAppt = this.createAppt.bind(this);
    this.updateDate = this.updateDate.bind(this);
    this.updateTime = this.updateTime.bind(this);
    this.printTimeRange = this.printTimeRange.bind(this);
  }

  printTimeRange(appt){
    let active = "";
    if(this.props.apptTime === appt)
      active = "active";
    return (
      <li key={appt} className={"list-group-item appt-item "+active} id={"time_"+appt} onClick={this.updateTime}>
        {moment(appt+":00", ["H:mm"]).format("h:mm A")+' - '+moment((appt+1)+":00", ["H:mm"]).format("h:mm A")}
      </li>
    );
  }


  updateDate(apptDate){
    date = apptDate;
    return this.setState({apptDate});
  }

  updateTime(e){
    const id = Number(e.target.id.substring(5));
    this.props.actions.updateTime(id);
  }

  createAppt(e){
    e.preventDefault();
    this.props.actions.createAppt();
  }

  render(){
    return (
      <Row>
        <Col md={2}/>
        <Col md={8}>
          <h2>
            Schedule An Appointment
            <button className="btn btn-primary pull-right" onClick={this.createAppt}>Make Appointment</button>
          </h2><hr/>
          <Row>
            <Col md={6}>
              <DatePicker inline
                selected={date}
                onChange={this.updateDate} />
            </Col>
            <Col md={6}>
              <ul className="list-group">
                {this.props.appointments && this.props.appointments.map(this.printTimeRange)}
              </ul>
            </Col>
          </Row>
        </Col>
      </Row>
    );
  }
}

AppointmentPage.propTypes = {
  actions: PropTypes.object.isRequired,
  user: PropTypes.object.isRequired,
  appointments: PropTypes.array,
  apptTime: PropTypes.number
};

export default connect(
  (s)=>{return {
    user:s.user,
    appointments:s.checkout.appointments,
    apptTime: s.checkout.apptTime
  };},
  d=>{return {actions:bindActionCreators(actions,d)};}
)(AppointmentPage);
person Daniel Murawsky    schedule 15.09.2016

Раньше у меня была такая же проблема, в моем случае это было вызвано неглубокой копией. Каждый раз, когда я возвращаю новое состояние, старое состояние также изменяется.

Я использовал хитрый способ заставить это работать, выполнив JSON.parse(JSON.stringify(temp)) для создания глубокого клона.

вы можете попробовать var querys = JSON.parse(JSON.stringify(state));также.

Надеюсь, что это работает.

person bianbian    schedule 10.10.2016