【问题标题】:componentDidMount: Can't call setState (or forceUpdate) on an unmounted componentcomponentDidMount:无法在未安装的组件上调用 setState(或 forceUpdate)
【发布时间】:2018-10-28 00:18:40
【问题描述】:

我正在 componentDidMount 中获取数据并更新状态,并且出现了著名的警告:

警告:无法在未安装的组件上调用 setState(或 forceUpdate)。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请在 componentWillUnmount 方法中取消所有订阅和异步任务。

我的代码如下:

componentDidMount() {
    let self = this;

    let apiBaseUrl = Config.serverUrl;
    axios.get( apiBaseUrl + '/dataToBeFetched/' )
        .then( function(response) {
            self.setState( { data: response.data } );;
        } );
}

是什么导致了这个警告?获取数据和更新状态的最佳方法是什么?

【问题讨论】:

    标签: reactjs axios


    【解决方案1】:

    根据之前的回答,我做了以下工作正常:

    constructor(props) {
       this.state = {isMounted: false}
    }
    
    componentDidMount() {
        let apiBaseUrl = Config.serverUrl;
        this.setState( { isMounted: true }, () => {
            axios.get( apiBaseUrl + '/dataToBeFetched/' )
                .then( (response) => { // using arrow function ES6
                    if( this.state.isMounted ) {
                        this.setState( { pets: response.data } );
                    }
                } ).catch( error => {
                    // handle error
            } )
        } );
    }
    
    componentWillUnmount() {
        this.setState( { isMounted: false } )
    }
    

    另一个更好的解决方案是取消卸载中的请求,如下所示:

    constructor(props) {
        this._source = axios.CancelToken.source();
    }
    
    componentDidMount() {
        let apiBaseUrl = Config.serverUrl;
        axios.get( apiBaseUrl + '/dataToBeFetched/', { cancelToken: this._source.token } )
            .then( (response) => { // using arrow function ES6
                if( this.state.isMounted ) {
                    this.setState( { pets: response.data } );
                }
            } ).catch( error => {
                // handle error
        } );
    }
    
    componentWillUnmount() {
        this._source.cancel( 'Operation canceled due component being unmounted.' )
    }
    

    【讨论】:

    • 您没有解释错误的原因。是在异步 get 调用和 setState 调用完成之前组件正在安装然后卸载?
    • 是的,没错。卸载不会等待异步调用完成。最终,当异步调用结束时,组件已经被卸载,因此它无法设置状态,无论如何您都不想这样做。为此,上面答案中的方法只是在设置状态之前检查组件是否已安装。
    • @O_k 谢谢!为什么需要在 this.setState 中执行 AJAX - 即 this.setState( { isMounted: true }, () => { ... ?
    • @dowi 这是为了确保在执行 axios 调用之前设置了 isMounted。使用 setState 不会立即改变状态。
    【解决方案2】:

    这很可能是因为在异步调用完成之前组件已经卸载。意思是,您在 axios 承诺中对 setState 的调用是在组件已卸载后调用的,可能是因为 react-router 重定向或状态更改?

    【讨论】:

      【解决方案3】:

      你可以试试这个:

      constructor() {
          super();
          this._isMounted = false;
      }
      
      componentDidMount() {
          this._isMounted = true;
          let apiBaseUrl = Config.serverUrl;
          this.setState( { isMounted: true }, () => {
              axios.get( apiBaseUrl + '/dataToBeFetched/' )
                  .then( (response) => { // using arrow function ES6
                      if( this._isMounted ) {
                          this.setState( { pets: response.data } );
                      }
                  } ).catch( error => {
                      // handle error
              } )
          } );
      }
      
      componentWillUnmount() {
          this._isMounted = false; // equals, not :
      }
      

      【讨论】:

      • 你能解释一下“this”的哪一部分有所不同吗?您应该这样做,以便其他人可以从您的答案中学习
      • 不需要设置状态,这段代码也可以不设置isMounted的状态。
      【解决方案4】:

      componentWillUnmount 上致电setState 是最糟糕的做法

      componentWillUnmount() {
          this.setState( { isMounted: false } ) // don't do this
      }
      

      【讨论】:

        猜你喜欢
        • 2023-04-08
        • 2018-11-03
        • 1970-01-01
        • 2019-03-10
        • 1970-01-01
        • 2019-01-07
        • 2019-01-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多