React Server-Side Rendering Memory Leak

1.9k views Asked by At

The code below according to the link react says that:

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,
    });
  };
}

I cant understand how this can be a memory leak in server-side. For example, lets say we have this code which is rendered server-side and the ExampleComponent contains the memory leak.

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

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

export default serverRender;

When this returns to the client, the rendered components are not attached to anywhere and are ready to be GB collected. So why is there a memory leak?

2

There are 2 answers

3
UjinT34 On BEST ANSWER

this.props.dataSource is something external and can live longer than the component calling subscribe. handleSubscriptionChange will be referenced by this.props.dataSource. Also the component itself might be referenced by this inside handleSubscriptionChange. So GB won't clean up ExampleComponent at all.

Since componentWillMount is deprecated you probably should not worry about these details and just use componentDidMaount.

0
apokryfos On

The answer is hinted on in the docs:

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

There's the obvious reason is that componentWillMount runs when component is about to mount so if mounting is interrupted then the component will never have mounted and therefore will never unmount. However there's also the hinted reason in the previous section in the docs which shows the code:

componentWillMount() {

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

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

} 

and says

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).

From this one can assume that componentWillMount does run during SSR and also runs when hydrating on the client-side meaning there's an additional unnecessary request being made causing a potential memory leak on the server.

However if you use componentDidMount then this is guaranteed to:

  1. Run only on the client side
  2. Ensure that componentWillUnmount will always run afterwards