ReactJS: setTimeout() not working?

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!

3

12 Answers

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
);
8
setTimeout(() => { this.setState({ position: 1 });
}, 3000);

The above would also work because the ES6 arrow function does not change the context of this.

1

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')
);
0

I know this is a little old, but is important to notice that React recomends to clear the interval when the component unmounts:

So I like to add this answer to this discussion:

 componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); }
0

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);

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.

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);
0

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)
0

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 😊!
}

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.

  1. pass a reference
setTimeout(this.doSomething, 500)
  1. 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)
  1. wrap in a standard anonymous function, but since it comes with it's own this, you must bind it to the this 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)

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);
}, []);

know more

Try to use ES6 syntax of set timeout. Normal javascript setTimeout() won't work in react js

setTimeout( () => this.setState({ position: 100 }), 5000 );
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like