Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Component with Memory Leak

I have a piece of legacy code, which renders a react component on the server on every request, which makes it obvious there is a memory leak. I have corner the problem up to this code:

  componentWillMount: function () {
    var onLogin = this.props.onLogin || function () {},
        onLogout = this.props.onLogout || function () {};

    this.on('authChange', function () {
      console.log('user authenticated:', this.state.isAuthenticated);
      return this.state.isAuthenticated
              ? onLogin(this.state)
              : onLogout(this.state);
    }.bind(this));
  },

I believe that on every request the this object is storing a new listener, but I don't get why the this element is not being marked as garbage when the rendering of the component is done.

like image 880
Mr. Goferito Avatar asked Oct 20 '16 16:10

Mr. Goferito


People also ask

How do you fix a memory leak in React class component?

This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. React can't detect memory leaks directly, but it introduces a warning to guide you to help figure them out on your own.

How do you prevent memory leak in React?

abort() after the request has been completed doesn't throw any errors. The abortController simply does not take any action on an already complete request. Using AbortControllers in your web apps can help improve performance and prevent memory leaks, so it's something you should actually use.

How do you identify a memory leak in React?

How to detect memory leaks? There is a way to detect memory leaks before you get the error from react. Simply use google chrome's developers tools. Go to Memory tab take heap snapshots and do a comparison between times before you took an action and after that.

Can memory leaks happen on stack?

Stack memory leaks occur when a method keeps getting called but never exits. This can happen if there is an infinite loop or if the method is being called with different data each time but the data is never used. Eventually, the stack will fill up and the program will run out of memory.


2 Answers

You need to unbind the authChange handler before the component is unmounted. You can do this in componentWillUnmount.

Since you're creating the handler function using the first props that are passed in, you should save it to a property so you can unbind it later:

  componentWillMount: function () {
    var onLogin = this.props.onLogin || function () {},
        onLogout = this.props.onLogout || function () {};

    this.authChange = function () {
      console.log('user authenticated:', this.state.isAuthenticated);
      return this.state.isAuthenticated
              ? onLogin(this.state)
              : onLogout(this.state);
    }.bind(this);

    this.on('authChange', this.authChange);
  },

  componentWillUnmount: function () {
      this.off('authChange', this.authChange);
      this.authChange = null;
  }

Note that when I saw this.on I thought you might be using jQuery but it's not clear how that would be the case. My answer uses this.off to detach the event listener but you should use whatever the corresponding method is in your framework.

like image 72
Tom Fenech Avatar answered Oct 23 '22 14:10

Tom Fenech


I would move your function into componentDidMount and add cleanup on componentWillUnmount

Important: componentWillMount is called on the server and client, but componentDidMount is only called on the client.

If you’re using eventListeners, setInterval or other functions that needs to be cleaned, put them in componentDidMount. The server will not call componentWillUnmount and is usually the cause of memory leaks.

like image 24
xiao Avatar answered Oct 23 '22 12:10

xiao