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 ответов


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

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

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

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

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)
);

больше

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