【问题标题】:Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application无法在未安装的组件上调用 setState(或 forceUpdate)。这是一个空操作,但它表明您的应用程序中存在内存泄漏
【发布时间】:2018-12-17 05:06:04
【问题描述】:

为什么会出现这个错误?

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

postAction.js

export const getPosts = () => db.ref('posts').once('value');

组件:

constructor(props) {
  super(props);
  this.state = { posts: null };
}

componentDidMount() {
  getPosts()
    .then(snapshot => {
      const result = snapshot.val();
      this.setState(() => ({ posts: result }));
    })
    .catch(error => {
      console.error(error);
    });
}

componentWillUnmount() {
  this.setState({ posts: null });
}

render() {
  return (
    <div>
      <PostList posts={this.state.posts} />
    </div>
  );
}

【问题讨论】:

  • 卸载的组件没有状态,不需要这样做。 React 已经告诉你这是一个 no-op
  • 你不应该在componentWillUnmount中调用this.setState。如果删除它,警告会消失吗?
  • 嘿,你可以在官方网站上阅读 react 生命周期钩子文档。它说当组件卸载时,不会进行任何状态更新,因为最终该组件的 UI 将被销毁
  • getPosts() 是异步方法。这就是错误。谢谢你们。谢谢@nicolas-tower

标签: javascript reactjs firebase


【解决方案1】:

正如其他人所提到的,componentWillUnmount 中的 setState 是不必要的,但它不应该导致您看到的错误。相反,可能的罪魁祸首是这段代码:

componentDidMount() {
  getPosts()
    .then(snapshot => {
      const result = snapshot.val();
      this.setState(() => ({ posts: result }));
    })
    .catch(error => {
      console.error(error);
    });
}

由于 getPosts() 是异步的,因此可能在它解析之前,组件已被卸载。您没有对此进行检查,因此 .then 可以在组件卸载后最终运行。

要解决这个问题,您可以在 willUnmount 中设置一个标志,并在 .then 中检查该标志:

componentDidMount() {
  getPosts()
    .then(snapshot => {
      if (this.isUnmounted) {
        return;
      }
      const result = snapshot.val();
      this.setState(() => ({ posts: result }));
    })
    .catch(error => {
      console.error(error);
    });
}

componentWillUnmount() {
  this.isUnmounted = true;
}

【讨论】:

  • 我不明白为什么这个解决方案如此流行。这只会让代码静默失败(即不显示结果)并消除警告。
  • @milosa 组件已卸载的事实意味着父组件已决定不再显示结果。因此,在大多数情况下,默默地不显示任何结果是预期的行为。如果您的需求不同,您当然可以采用不同的方法。
【解决方案2】:

React 组件的状态是一个本地实体。卸载的组件没有状态,不需要这样做。 React 已经告诉你这是一个no-op,这意味着在技术方面没有操作。这意味着你告诉组件在它已经被销毁时做某事。

https://reactjs.org/docs/react-component.html#componentwillunmount

你不应该在 componentWillUnmount() 中调用 setState(),因为组件永远不会被重新渲染。一旦组件实例被卸载,它将永远不会被再次装载。

【讨论】:

    【解决方案3】:

    删除这个

    componentWillUnmount() {
      this.setState({ posts: null });
    }
    

    没用的

    来自文档:

    您不应该在 componentWillUnmount() 中调用 setState(),因为 组件永远不会被重新渲染。一旦组件实例 已卸载,它将永远不会再次安装。

    https://reactjs.org/docs/react-component.html#componentwillunmount

    【讨论】:

      【解决方案4】:

      你可以试试这个代码:

      constructor(props) {
        super(props);
        this.state = { posts: null };
      }
      
      _isMounted = false;
      componentDidMount() {
        this._isMounted = true;
        getPosts()
          .then(snapshot => {
            const result = snapshot.val();
            if(this._isMounted) { 
                this.setState(() => ({ posts: result }))
            }
          })
          .catch(error => {
            console.error(error);
          });
      }
      
      componentWillUnmount() {
        this._isMounted = false;
        this.setState({ posts: null });
      }
      
      render() {
        return (
          <div>
            <PostList posts={this.state.posts} />
          </div>
        );
      }
      

      通过使用 _isMounted,setState 仅在组件已安装时才被调用。答案只是在设置状态之前检查组件是否已安装。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-14
        • 2020-06-27
        • 2021-08-09
        • 1970-01-01
        • 2021-11-14
        • 2023-04-08
        • 1970-01-01
        • 2021-12-09
        相关资源
        最近更新 更多