setTimeout not fully cancelled before component unmounts

Multi tool use
Clash 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)
);
);
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.
You never clear the timer anywhere. Don't call
setState
incomponentWillUnmount
, just callclearTimeout
if necessary.– Evan Trimboli
Aug 7 at 11:35