134

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!

revelt
  • 2,312
  • 1
  • 25
  • 37
jbarradas
  • 2,031
  • 3
  • 17
  • 22
  • 3
    If you have `foo(bar())` then `bar` is **executed first** and its return value is passed to `foo`. – Felix Kling Mar 28 '16 at 20:24
  • @FelixKling that seems correct, but not appropriate. Since the `foo()` here is exactly to execute `bar` after desired timeout. Or am I completely wrong and it executes right away, and only return value after the desired time? – jbarradas Mar 28 '16 at 20:52
  • 4
    *"Since the foo() here is exactly to execute bar after desired timeout."* Right, that's why you have to pass `bar`, not call it and pass its returns value. Did you expect the behavior of `foo(bar())` to change, depending what `foo` is doing? That would be really strange. – Felix Kling Mar 28 '16 at 21:36

12 Answers12

293

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
);
Reg Edit
  • 6,719
  • 1
  • 35
  • 46
Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • 1
    Yeah makes sense and it's working. But isn't function() a function? So why would we need to bind it? I already tried and it's really needed, I just wanted to know why. Appreciate for your help :) – jbarradas Mar 28 '16 at 20:33
  • I don't get why you're saying it would pass the result to setTimeout, how does that not make this work? What's the behavior in that case? – PositiveGuy Aug 18 '16 at 04:32
  • 16
    for those of you who prefer to use ES6 arrow functions: `setTimeout(() => {this.setState({ position: 1 })}, 3000)` @PositiveGuy not sure if you've researched this on your own since this question was posted, but in case you haven't: Daniel's original example needs `.bind(this)` to restrict the `this` context to `setState` - otherwise, `this` will automatically refer to the context in which it is invoked (in this case, the anonymous `function` being passed to `setTimeout`). ES6 arrow functions, however, are _lexically scoped_ - they restrict `this` to the context in which they're called. – Zac Collier Dec 21 '16 at 23:07
  • 1
    Doesn't work... setTimeout(() => { if (!this.props.logoIsLoading && !this.props.isLoading) { console.log('Will we happen?'); this.setState({ ...this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Add New Organization' }); } }, 100); Its in context of class syntactical sugar class Organizations extends Component { console.log never gets console.log('Will we happen?'); Everything before it and after it is logged. – juslintek Mar 16 '17 at 14:35
  • @juslintek define not work. please ask a new question if needed. – Daniel A. White Mar 16 '17 at 14:35
  • @Daniel A. White, I use redux-saga and redux-store, might they be affecting my outcome? – juslintek Mar 16 '17 at 14:39
  • @juslintek no clue. please ask a new top level question, not in a comment to my answer. – Daniel A. White Mar 16 '17 at 14:39
  • Pure GEM of an answer – sg28 Mar 04 '21 at 09:44
  • Please don't. Use this instead: `setTimeout(this.setState, 3000, { position: 1 })`. [See below](https://stackoverflow.com/questions/36270422/reactjs-settimeout-not-working/66694391#66694391) – Qwerty Dec 07 '22 at 14:18
167
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

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

Steven Scaffidi
  • 2,280
  • 1
  • 12
  • 14
  • 3
    The ES6 syntax should be the accepted answer for best practices in React. Both will work, but this is more elegant and handles `this`. – mccambridge Aug 01 '17 at 13:51
26

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')
);
Khalid Azam
  • 1,615
  • 19
  • 17
19

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);
  }
Fernando Lopes
  • 692
  • 6
  • 11
10

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);
tymeJV
  • 103,943
  • 14
  • 161
  • 157
9

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.

Advis
  • 141
  • 2
  • 2
5

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);
Xin
  • 33,823
  • 14
  • 84
  • 85
4

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)
Darryl Fabian
  • 111
  • 2
  • 7
3

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 ! 
}
KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
3

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 "a is not a function"
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() {/* */}
const a = 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 yet another function. That is two additional functions every time you call it.
setTimeout(function(){this.doSomething()}.bind(this), 500)
Qwerty
  • 29,062
  • 22
  • 108
  • 136
2
  useEffect(() => {
    setTimeout(() => setActive(true), 5000);
    
  },[]);
  • 2
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Oct 25 '22 at 08:48
  • Please explain little bit about answer – Pradeep Kumar Oct 26 '22 at 06:45
0

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

setTimeout(
      () => this.setState({ position: 100 }), 
      5000
    );
Codemaker2015
  • 12,190
  • 6
  • 97
  • 81