5

I am facing the following issue:

Inside a react component that handles state, there is a websocket connection with socket.io. Whenever I receive some events I should do some operation and update the state of the component. Example:

socket.on('some event', () => {
 this.aMethod() // this method calls setState()
})

Problem is that the following message appears:

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

But I am not using componentWillUnmount method anywhere. And the component is not unmounted.

If I replace the aMethod with this.props.aMethod() and update the state of the parent component that will work ( aMethod exists in both components ). The problem is that this component is huge because it's a container and moving all state to parent and using props would cause a lot of rewrite..

That gave me an idea that there might be some problem with react routes of the app.

The parent component has the following routes ( conceptually ):

 <Switch>

          <Route exact path={'/user/:id'} render={(props) => <User />} />
          <Route exact path={'/users'} render={(props) => <User />} />

  </Switch>

I used render instead of component because I need to pass some methods as props. React and react-router-dom is at the very latest version of 16.x and 5.0.0.

Is it possible somehow react kind of "freezes" the first instance of the component and then url changes and I am not able to update the state anymore ? If i console.log a message inside componentWillUnmount() i don't see it unmounting.

Any idea would be much appreciated!

Feel free to ask questions.

DrNio
  • 1,936
  • 1
  • 19
  • 25

1 Answers1

7

React is telling you that you MUST delete the listener in componentWillUnmount:

componentWillUnmount() {
 socket.off('some event');
}

This is due to the fact that:

  • if you add the listener in componentDidMount you'll end up with multiple listeners
  • when component is unmounted (on route change) "this" and its context binding does not make sense anymore, but the callback tries anyway to set the state every time the event occurs

Form react docs:

componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount().

Ref: https://reactjs.org/docs/react-component.html?utm_source=caibaojian.com#componentwillunmount

Another resource to better understand the issue:

https://advancedweb.hu/2016/01/05/global-listener-patterns-in-react/

Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
  • you were right.. i had the socket connection in the constructor but i never disconnected of removing listener from an event with `off`. still not sure how this affected the `setState`, why `this` works and why `this.props` also worked :) any further guidance or link would be helpful. – DrNio May 03 '19 at 12:39
  • i also used .disconnect() in the `componentWillUnmount`. In case I want to leave the connection open and close it on logout would that be any issue ? – DrNio May 03 '19 at 12:40
  • once you remove the listener you're done. I've added the relevant part of react docs – Mosè Raguzzini May 03 '19 at 12:42