React - 在未安装的组件上的setState()

Mar*_*rty 83 javascript ajax state reactjs

在我的react组件中,我试图在ajax请求正在进行时实现一个简单的微调器 - 我使用state来存储加载状态.

出于某种原因,我的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>
      );
  }
Run Code Online (Sandbox Code Playgroud)

问题是为什么我应该已经安装组件时出现此错误(因为它是从componentDidMount调用的)我认为一旦组件安装后设置状态是安全的?

小智 66

没有看到渲染功能有点困难.虽然已经可以发现你应该做的事情,但每次你使用一个间隔时你必须在卸载时清除它.所以:

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

componentWillUnmount () {
    this.loadInterval && clearInterval(this.loadInterval);
    this.loadInterval = false;
}
Run Code Online (Sandbox Code Playgroud)

由于在卸载后仍可能会调用这些成功和错误回调,因此可以使用interval变量来检查它是否已挂载.

this.loadInterval && this.setState({
    loading: false
});
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助,如果不能完成这项任务,请提供渲染功能.

干杯

  • 或者简单地说:`componentWillUnmount(){clearInterval(this.loadInterval); }` (6认同)
  • 布鲁诺,你不能只测试"这个"上下文的存在.. ala this && this.setState ..... (2认同)

Mar*_*tus 14

问题是为什么我应该已经安装组件时出现此错误(因为它是从componentDidMount调用的)我认为一旦组件安装后设置状态是安全的?

不是来自componentDidMount.你componentDidMount产生一个回调函数,它将在计时器处理程序的堆栈中执行,而不是在堆栈中执行componentDidMount.显然,当你的callback(this.loadSearches)被执行时,组件已卸载.

所以接受的答案会保护你.如果您正在使用其他一些不允许您取消异步函数的异步API(已经提交给某个处理程序),您可以执行以下操作:

if (this.isMounted())
     this.setState(...
Run Code Online (Sandbox Code Playgroud)

虽然它确实感觉像地毯下扫的东西,特别是如果你的API提供了一个取消能力(因为这将摆脱您在任何情况下报告该错误消息的setInterval与做clearInterval).

  • `isMounted`是facebook建议不使用的反模式:https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html (12认同)

bur*_*kan 5

对于谁需要另一个选项,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>
 )
}
Run Code Online (Sandbox Code Playgroud)

  • 使用ref有效地"isMounted"与使用isMounted完全相同但不太清楚.isMounted因其名称而不是反模式,但因为它是一种反模式来保存对未安装组件的引用. (5认同)