React-setState () на размонтированном компоненте


в моем компоненте react im пытается реализовать простой счетчик, пока выполняется запрос ajax-im использует состояние для хранения состояния загрузки.

по какой-то причине этот кусок кода ниже в моем компоненте React выдает эту ошибку

можно обновить только смонтированный или монтажный компонент. Это обычно означает вы вызвали setState () для размонтированного компонента. Это не операция. Пожалуйста, проверьте код для неопределенного деталь.

Если я избавлюсь от первого вызова setState, ошибка исчезнет.

constructor(props) {
  super(props);
  this.loadSearches = this.loadSearches.bind(this);

  this.state = {
    loading: false
  }
}

loadSearches() {

  this.setState({
    loading: true,
    searches: []
  });

  console.log('Loading Searches..');

  $.ajax({
    url: this.props.source + '?projectId=' + this.props.projectId,
    dataType: 'json',
    crossDomain: true,
    success: function(data) {
      this.setState({
        loading: false
      });
    }.bind(this),
    error: function(xhr, status, err) {
      console.error(this.props.url, status, err.toString());
      this.setState({
        loading: false
      });
    }.bind(this)
  });
}

componentDidMount() {
  setInterval(this.loadSearches, this.props.pollInterval);
}

render() {

    let searches = this.state.searches || [];


    return (<div>
          <Table striped bordered condensed hover>
          <thead>
            <tr>
              <th>Name</th>
              <th>Submit Date</th>
              <th>Dataset &amp; Datatype</th>
              <th>Results</th>
              <th>Last Downloaded</th>
            </tr>
          </thead>
          {
          searches.map(function(search) {

                let createdDate = moment(search.createdDate, 'X').format("YYYY-MM-DD");
                let downloadedDate = moment(search.downloadedDate, 'X').format("YYYY-MM-DD");
                let records = 0;
                let status = search.status ? search.status.toLowerCase() : ''

                return (
                <tbody key={search.id}>
                  <tr>
                    <td>{search.name}</td>
                    <td>{createdDate}</td>
                    <td>{search.dataset}</td>
                    <td>{records}</td>
                    <td>{downloadedDate}</td>
                  </tr>
                </tbody>
              );
          }
          </Table >
          </div>
      );
  }

вопрос в том, почему я получаю эту ошибку, когда компонент уже должен быть смонтирован (поскольку он вызывается из componentDidMount) я думал, что безопасно установить состояние после монтирования компонента ?

4 70

4 ответа:

не видя функцию рендеринга немного сложно. Хотя вы уже можете определить, что вам нужно сделать, каждый раз, когда вы используете интервал, вы должны очистить его при отключении. Итак:

componentDidMount() {
    this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval);
}

componentWillUnmount () {
    this.loadInterval && clearInterval(this.loadInterval);
    this.loadInterval = false;
}

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

this.loadInterval && this.setState({
    loading: false
});

надеюсь, что это поможет, обеспечить функцию рендеринга, если это не делает работу.

Ура

вопрос в том, почему я получаю эту ошибку, когда компонент уже должен быть смонтирован (поскольку он вызывается из componentDidMount) я думал, что безопасно установить состояние после монтирования компонента ?

это не позвонили из componentDidMount. Ваш componentDidMount запускает функции обратного вызова, которая будет выполняться в стеке обработчика таймера, а не в стеке componentDidMount. Видимо, к моменту вашего обратного звонка (this.loadSearches) выполняется в компонент размонтирован.

таким образом, принятый ответ защитит вас. Если вы используете какой-либо другой асинхронный API, который не позволяет вам отменять асинхронные функции (уже отправленные какому-либо обработчику), вы можете сделать следующее:

if (this.isMounted())
     this.setState(...

это позволит избавиться от сообщения об ошибке, которое вы сообщаете во всех случаях, хотя это действительно похоже на подметание вещей под ковром, особенно если ваш API предоставляет возможность отмены (как setInterval С clearInterval).

кому нужна другая опция, метод обратного вызова атрибута ref может быть обходным путем. Параметр handleRef-это ссылка на элемент div DOM.

для получения подробной информации о refs и DOM:https://facebook.github.io/react/docs/refs-and-the-dom.html

handleRef = (divElement) => {
 if(divElement){
  //set state here
 }
}

render(){
 return (
  <div ref={this.handleRef}>
  </div>
 )
}

для потомков,

эта ошибка, в нашем случае, была связана с рефлюксом, обратными вызовами, перенаправлениями и setState. Мы отправили выполнении функция setState к onDone обратного вызова, но мы также направили редирект на onsuccess процедуры обратного вызова. В случае успеха, наш обратный вызов onSuccess выполняется перед onDone. Это вызывает перенаправление перед попыткой setState. Таким образом, ошибка, setState на размонтированном компоненте.

рефлюкс магазине действие:

generateWorkflow: function(
    workflowTemplate,
    trackingNumber,
    done,
    onSuccess,
    onFail)
{...

звонить до исправления:

Actions.generateWorkflow(
    values.workflowTemplate,
    values.number,
    this.setLoading.bind(this, false),
    this.successRedirect
);

звонить после исправления:

Actions.generateWorkflow(
    values.workflowTemplate,
    values.number,
    null,
    this.successRedirect,
    this.setLoading.bind(this, false)
);

больше

в некоторых случаях, поскольку React isMounted является "устаревшим / анти-шаблоном", мы приняли использование переменной _mounted и сами ее отслеживаем.