【问题标题】:How function exist after component has been unmounted卸载组件后功能如何存在
【发布时间】:2017-12-23 02:20:31
【问题描述】:

我的疑问与在反应组件中使用计时器有关,据我了解一旦组件卸载之后,其所有属性/方法将不存在。

根据DOC

componentWillUnmount() 在组件被调用之前立即调用 卸载并销毁。 在此方法中执行任何必要的清理, 例如使计时器无效、 取消网络请求或清理 在 componentDidMount 中创建的任何 DOM 元素。

检查这个sn-p:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      3000
    );
  }

  componentWillUnmount() {
    //clearInterval(this.timerID);
  }

  tick() {
     console.log('called', this.props.no);
  }

  render() {
    return (
      <div>
        <h1>Clock {this.props.no}</h1>
      </div>
    );
  }
}

class App extends React.Component {
  
  constructor(){
     super();
     this.state = {unMount: false}
  }
  
  click(){
     console.log('unmounted successfully');
     this.setState({unMount: !this.state.unMount})
  }
  
  render(){
    return (
       <div>
          <button onClick={() => this.click()}>Unmount first</button>
           {!this.state.unMount && <Clock no={1}/>}
           <Clock no={2}/>
       </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='root'/>

在这里,我正在渲染两个时钟组件并卸载第一个 onclick 成功发生的按钮,并且它也在更新 DOM,即使在卸载第一个组件后,计时器正在通过 console.log() 正确打印道具值。

我没有清除componentWillUmount中的定时器

componentWillUnmount() {
    //clearInterval(this.timerID);
}

我的疑问是:

this.timerID = setInterval(
   () => this.tick(),
   3000
);

tick() {
   console.log('called', this.props.no);
}

我在计时器中传递了一个类方法作为回调,所以一旦组件被卸载,tick 函数如何存在,这个计时器如何解析this 关键字和组件卸载后的tick 函数? this.props.no 如何具有正确的值?为什么它没有抛出错误:

无法读取未定义的刻度或未定义刻度

它是如何维护对这些函数的引用的?

帮助我在这里缺少什么,请提供任何参考或示例。

【问题讨论】:

  • ..根据我的理解,一旦组件卸载,其所有属性/方法将不存在。这不太正确。从技术上讲,React 组件是一个 JS 类(函数),mounting 在这个类和相应的 DOM 东西之间建立了联系。卸载组件时,仅此连接被破坏,但相应的 JS 类及其道具/方法仍然存在
  • @hindmost 谢谢,但是如果该代码仍然存在,那么它应该会产生内存问题,因为当我们访问不同的页面时组件会非常频繁地渲染和卸载,对吗?另一件事是,直到什么时候该代码才可用,因为我等了 10 分钟,但仍然可以正确打印值,一段时间后它应该会抛出错误。

标签: javascript reactjs


【解决方案1】:

对 React 代码做了很多挖掘,我发现了以下 doc

以及来自 react github 页面的代码

function unmountComponentFromNode(instance, container) {
  if (__DEV__) {
    ReactInstrumentation.debugTool.onBeginFlush();
  }
  ReactReconciler.unmountComponent(
    instance,
    false /* safely */,
    false /* skipLifecycle */,
  );
  if (__DEV__) {
    ReactInstrumentation.debugTool.onEndFlush();
  }

  if (container.nodeType === DOCUMENT_NODE) {
    container = container.documentElement;
  }

  // http://jsperf.com/emptying-a-node
  while (container.lastChild) {
    container.removeChild(container.lastChild);
  }
}

这表明 React 将从 UI 中移除组件并为垃圾收集做好准备,但是即使移除了 DOM 元素 类实例不是这样的方法,道具仍然存在,因此 setInterval 继续执行。

只有当this.ticks()this.props的引用不再被使用时,它们才有资格进行垃圾回收

JavaScript 值是在事物(对象、字符串等)被分配时分配的 当它们不再使用时,它们会被创建并“自动”释放。 后一个过程称为垃圾收集。

【讨论】:

  • 同意,但是垃圾收集器会在 x 分钟后清除它,之后它应该会抛出错误,但这并没有发生,它正在正确打印值。
  • 如果它们不再使用,它​​们将是,但 setInterval 使用它们,因此它们不能用于垃圾收集
【解决方案2】:

与 C++ 不同,您不能在 JavaScript 中显式地从内存中删除对象(也称为“销毁”)。您只能删除对它们的引用。一旦不再有指向对象或其属性的引用,它就有资格进行垃圾回收。只有这样垃圾收集器才会真正销毁它。

Memory Management — JavaScript

在这种情况下,即使在卸载之后,您的闭包中仍然有对该对象及其属性的有效引用(this.tickthis.props 等)。在某些时候,执行后,它们会超出范围,然后您的组件将被销毁并释放内存。

【讨论】:

    【解决方案3】:

    这超出了react 的工作方式,更多的是javascript。您应该在componentWillUnmount 上清除您的时间间隔。它甚至在文档中说:

    在此方法中执行任何必要的清理,例如使 计时器

    您的代码被注释掉了?这不是这里的问题吗?

      componentWillUnmount() {
        //clearInterval(this.timerID);
      }
    

    【讨论】:

    • 这是我的问题,我没有清除计时器及其打印值,而是它如何解析 this 关键字,因为我将类方法作为回调传递.
    • 因为它在内存中有一个引用,所以您传递了lexical this,当您执行() =&gt; 时,它绑定了this 的副本。当文档明确说您应该在那里进行清理时,我不会太担心它为什么不这样做。
    • 是的,我也关注文档,但只是想知道它发生的原因和原因,感谢您的时间:)
    猜你喜欢
    • 2021-10-08
    • 1970-01-01
    • 2021-07-26
    • 2019-12-28
    • 2019-10-02
    • 2022-08-12
    • 2019-09-09
    • 2023-01-14
    • 2012-12-04
    相关资源
    最近更新 更多