ReactJS: setTimeout() not working?
JavascriptReactjsJavascript Problem Overview
Having this code in mind:
var Component = React.createClass({
getInitialState: function () {
return {position: 0};
},
componentDidMount: function () {
setTimeout(this.setState({position: 1}), 3000);
},
render: function () {
return (
<div className="component">
{this.state.position}
</div>
);
}
});
ReactDOM.render(
<Component />,
document.getElementById('main')
);
Isn't the state supposed to change only after 3 seconds? It's changing immediately.
My main goal here is to change the state every 3 seconds (with setInterval()
), but since it was not working, I tried setTimeout()
, which is not working either. Any lights on this? Thanks!
Javascript Solutions
Solution 1 - Javascript
Do
setTimeout(
function() {
this.setState({ position: 1 });
}
.bind(this),
3000
);
Otherwise, you are passing the result of setState
to setTimeout
.
You can also use ES6 arrow functions to avoid the use of this
keyword:
setTimeout(
() => this.setState({ position: 1 }),
3000
);
Solution 2 - Javascript
setTimeout(() => {
this.setState({ position: 1 });
}, 3000);
The above would also work because the ES6 arrow function does not change the context of this
.
Solution 3 - Javascript
Anytime we create a timeout we should s clear it on componentWillUnmount, if it hasn't fired yet.
let myVar;
const Component = React.createClass({
getInitialState: function () {
return {position: 0};
},
componentDidMount: function () {
myVar = setTimeout(()=> this.setState({position: 1}), 3000)
},
componentWillUnmount: () => {
clearTimeout(myVar);
};
render: function () {
return (
<div className="component">
{this.state.position}
</div>
);
}
});
ReactDOM.render(
<Component />,
document.getElementById('main')
);
Solution 4 - Javascript
I know this is a little old, but is important to notice that React recomends to clear the interval when the component unmounts: https://reactjs.org/docs/state-and-lifecycle.html
So I like to add this answer to this discussion:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
Solution 5 - Javascript
setState
is being invoked immediately due to the parenthesis! Wrap it in an anonymous function, then call it:
setTimeout(function() {
this.setState({position: 1})
}.bind(this), 3000);
Solution 6 - Javascript
You didn't tell who called setTimeout
Here how you call timeout without calling additional functions.
1. You can do this without making additional functions.
setTimeout(this.setState.bind(this, {position:1}), 3000);
Uses function.prototype.bind()
setTimeout takes the location of the function and keeps it in the context.
2. Another way to do the same even by writing even less code.
setTimeout(this.setState, 3000, {position:1});
Probably uses the same bind method at some point
The setTimeout only takes the location of the function and the function already has the context? Anyway, it works!
NOTE: These work with any function you use in js.
Solution 7 - Javascript
Your code scope (this
) will be your window
object, not your react component, and that is why setTimeout(this.setState({position: 1}), 3000)
will crash this way.
That comes from javascript not React, it is js closure
So, in order to bind your current react component scope, do this:
setTimeout(function(){this.setState({position: 1})}.bind(this), 3000);
Or if your browser supports es6 or your projs has support to compile es6 to es5, try arrow function as well, as arrow func is to fix 'this' issue:
setTimeout(()=>this.setState({position: 1}), 3000);
Solution 8 - Javascript
There's a 3 ways to access the scope inside of the 'setTimeout' function
First,
const self = this
setTimeout(function() {
self.setState({position:1})
}, 3000)
Second is to use ES6 arrow function, cause arrow function didn't have itself scope(this)
setTimeout(()=> {
this.setState({position:1})
}, 3000)
Third one is to bind the scope inside of the function
setTimeout(function(){
this.setState({position:1})
}.bind(this), 3000)
Solution 9 - Javascript
You did syntax declaration error, use proper setTimeout declaration
message:() => {
setTimeout(() => {this.setState({opened:false})},3000);
return 'Thanks for your time, have a nice day 😊!
}
Solution 10 - Javascript
Just pass the function as a reference, no need to wrap it in an anonymous function or even bind it, which creates yet another function.
setTimeout(this.setState, 500, {position: 1});
Enter setTimeout
It seems people don't realise that setTimeout
and setInterval
 actually accept optional unlimited parameters.
setTimeout(callback, timeout?, param1?, param2?, ...)
The reason is to make calling the callback simpler, so instead of this
setTimeout(
function(){
this.doSomething(true, "string", someVariable)
}.bind(this),
500
)
You can write this
setTimeout(this.doSomething, 500, true, "string", someVariable)
Isn't that beautiful and elegant? 
Bug?
There is no bug in React calling setTimeout instantly, so if you were puzzled by it, consider this.
function doSomething() {/* */}
const a = doSomething() // immediately invokes and assigns a result
const b = doSomething // stores a reference for later call
// call later
const x = a() // error
const y = b() // invokes doSomething and assigns a result
And in your case with setState
, this is basically the same thing.
When you register your setTimeout
callback, you mistakenly immediately call it, where instead you should pass a reference to it.
function doSomething() {/* */}
// wrong
setTimeout(doSomething(), 500) // This is basically the same as writing the `a` from above
setTimeout(a, 500) // like this. See the problem? a() cannot be called later.
To fix it, you have three options.
- pass a reference
setTimeout(this.doSomething, 500)
- wrap in an anonymous arrow function which is transparent to
this
,
meaning it captures the outer (parent)this
.
note that this wraps your function in another function every time you call this
setTimeout(() => this.doSomething(), 500)
- wrap in a standard anonymous function, but since it comes with it's own
this
, you must bind it to thethis
of the parent.
note that this wraps your function in another function AND THEN binds it, which creates a third function every time
setTimeout(function(){this.doSomething()}.bind(this), 500)
Solution 11 - Javascript
Passing string literals Passing a string instead of a function to setTimeout() has the same problems as using eval().
componentDidMount: function () {
// Do this instead
setTimeout(function() {
console.log('Hello World!');
}, 500);
}
Using react hook
useEffect(() => {
const timer = setTimeout(() => {
console.log('This will run after 1 second!')
}, 1000);
return () => clearTimeout(timer);
}, []);
Solution 12 - Javascript
Try to use ES6 syntax of set timeout. Normal javascript setTimeout() won't work in react js
setTimeout(
() => this.setState({ position: 100 }),
5000
);