setTimeout not fully cancelled before component unmounts

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



setTimeout not fully cancelled before component unmounts



Warning: Can't call setState (or forceUpdate) 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.


Warning: Can't call setState (or forceUpdate) 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.



Why am I still getting an error about setting state in an unmounted component? In the error trace it points to the setTimeout in foo(). I clear my async timer and also add a check before I perform my api call - I don't see where this warning is coming from.


setTimeout


foo()


componentDidMount()
this.setState(alive: true);
this.foo();


componentWillUnmount()
this.setState(alive: false, timer: 0);


foo()

if (!this.state.alive) return;
fetch('/api/etc/', method: 'GET', headers: 'Cache-Control': 'no-cache')
.then(res => res.json())
.then(json =>

if (!json.length) return;

this.setState((prevState) => (
timer: setTimeout(this.foo.bind(this), 500)
);
);





You never clear the timer anywhere. Don't call setState in componentWillUnmount, just call clearTimeout if necessary.
– Evan Trimboli
Aug 7 at 11:35


setState


componentWillUnmount


clearTimeout





this works, thanks. whatever i was reading said to set timer to 0.
– chris
Aug 7 at 11:38




1 Answer
1



* Updated Answer *
As Evan Trimboli pointed out in the comment below, there is no need to store the timeout ID in the state as it doesn't impact rendering.


it doesn't impact rendering



So store the timeout ID in the class instance and use it to clear timeout in componentWillUnmount.


timeout


componentWillUnmount



Run the code below to see it in action




class TodoApp extends React.Component
timeout = 0;

hello = () => console.log("hello world!")

componentDidMount()
this.timeout = setTimeout(this.hello, 500);


componentWillUnmount()
clearTimeout(this.timeout);


render()
return (
<div>demo</div>
)



ReactDOM.render(<TodoApp />, document.querySelector("#app"))


<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="app"></div>



Store the timeout ID in the state, and use that to clear out the timeout in componentWillUnmount.


componentWillUnmount




class TodoApp extends React.Component
constructor(props)
super(props)
this.state =
timeout: null


this.hello = this.hello.bind(this);


hello() console.log("hello world!");

componentDidMount()
const timeout = setTimeout(this.hello, 500);
this.setState(timeout);


componentWillUnmount()
clearTimeout(this.state.timeout);


render()
return (
<div>demo</div>
)



ReactDOM.render(<TodoApp />, document.querySelector("#app"))


<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="app"></div>





It's probably better to store the timer on the class instance, since it won't impact rendering.
– Evan Trimboli
Aug 8 at 2:26





@EvanTrimboli You are absolutely right. I've updated the answer according to your suggestion. Thanks Evan 🙏
– Sung Kim
Aug 8 at 3:01






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard