React 服务器端渲染内存泄漏

React Server-Side Rendering Memory Leak

下面的代码根据 link react 表示:

Unfortunately, this can cause memory leaks for server rendering (where componentWillUnmount will never be called)

// Before
class ExampleComponent extends React.Component {
  componentWillMount() {
    this.setState({
      subscribedValue: this.props.dataSource.value,
    });

    // This is not safe; it can leak!
    this.props.dataSource.subscribe(
      this.handleSubscriptionChange
    );
  }

  componentWillUnmount() {
    this.props.dataSource.unsubscribe(
      this.handleSubscriptionChange
    );
  }

  handleSubscriptionChange = dataSource => {
    this.setState({
      subscribedValue: dataSource.value,
    });
  };
}

我不明白这怎么会是服务器端的内存泄漏。例如,假设我们有这段在服务器端呈现的代码,并且 ExampleComponent 包含内存泄漏。

import React from 'react';
import ReactDomServer from 'react-dom/server';
import App from './components/index'

const serverRender =  () =>{
    return ReactDomServer.renderToString(<ExampleComponent />);
};

export default serverRender;

当这个 returns 到客户端时,渲染的组件不会附加到任何地方,并准备好被 GB 收集。那么为什么会出现内存泄漏呢?

this.props.dataSource 是外部的东西,比调用 subscribe 的组件寿命更长。 handleSubscriptionChange 将被 this.props.dataSource 引用。此外,组件本身可能被 handleSubscriptionChange 中的 this 引用。所以 GB 根本不会清理 ExampleComponent

由于 componentWillMount 已弃用,您可能不必担心这些细节,只需使用 componentDidMaount.

文档中暗示了答案:

People often assume that componentWillMount and componentWillUnmount are always paired, but that is not guaranteed

很明显的原因是 componentWillMount 运行s 当组件 即将 安装时,如果安装被中断,那么组件将永远不会安装因此永远不会卸载。然而,在显示代码的文档的上一节中也有暗示的原因:

componentWillMount() {

    this.setState({
      subscribedValue: this.props.dataSource.value,
    });

    // This is not safe; it can leak!
    this.props.dataSource.subscribe(
      this.handleSubscriptionChange
    );

} 

并说

The above code is problematic for both server rendering (where the external data won’t be used) and the upcoming async rendering mode (where the request might be initiated multiple times).

由此可以假设 componentWillMount 在 SSR 期间执行 运行 并且在客户端补水时 运行 执行,这意味着发出了额外的不必要请求,导致潜在的服务器内存泄漏。

但是,如果您使用 componentDidMount,则可以保证:

  1. 运行 仅在客户端
  2. 确保 componentWillUnmount 之后总是 运行