19

I'm sending values of numbers from Numbers component to Main component. Everything is working fine until I set that value in my Main component to that component's state.

var Numbers = React.createClass({
    handleClick: function(number){
        this.props.num(number)
    },
    render: function(){
        return (
            <div>
                <button onClick={this.handleClick.bind(null, 1)}>1</button>
                <button onClick={this.handleClick.bind(null, 2)}>2</button>
                <button onClick={this.handleClick.bind(null, 3)}>3</button>
            </div>
        )
    }
})

var Main = React.createClass({
    getInitialState: function(){
        return {
            number: 0
        }
    },
    handleCallback: function(num){
        console.log("number is right here: " + num);
        this.setState({
            number: num
        })
        console.log("but wrong here (previous number): " + this.state.number)
    },
    render: function() {
        return (
            <Numbers num={this.handleCallback} />
            //<SomeComponent number={this.state.number} />
        )
    }
});

React.render(<Main />, document.getElementById('container'));

Why is it behaving like this? Second console.log in handleCallback function prints the previous number, not the number which is in num parameter. I need right number to be in my state, because I'm going to send it immediately as an props in my SomeComponent component.

https://jsfiddle.net/69z2wepo/13000/

StrangeManInMask
  • 203
  • 1
  • 2
  • 4

5 Answers5

32

From the docs:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

If you want to print the change after a call to setState, use the optional callback parameter:

this.setState({
    number: num
}, function () {
    console.log(this.state.number);
});
Michael Parker
  • 12,724
  • 5
  • 37
  • 58
  • How would you solve out the problem with `SomeComponent` ? I now get why it isn't working, but I have no idea how I can pass the state as an props only when this.state has been updated. Can I somehow wrap that `}` to callback or something? – StrangeManInMask Jul 29 '15 at 14:34
  • Changing a component's state or props forces a re-render, unless you have implemented `shouldComponentUpdate` to tell it otherwise. Thus, once the state has changed in `
    `, it will re-render, and pass the new number to `` as a new prop. If you have implemented `componentWillReceiveProps` in ``, you will see the number in the received props. Does this make sense? Or would you like me to modify my answer and go a little more in depth?
    – Michael Parker Jul 29 '15 at 14:40
  • You can go in depth if you have time / interest. I would myself really appreciate it. My problem with `` is that it doesn't wait for `
    `'s re-render (I believe..) so the right value from state isn't sent. I don't know how to implement that callback you showed for that. Maybe this explains a little bit better: https://jsfiddle.net/69z2wepo/13007/
    – StrangeManInMask Jul 29 '15 at 15:05
  • Your problem is that you're alerting the current props in `` instead of the new ones. The function `componentWillReceiveProps` can take a parameter, `nextProps`, which contains all of the new props that were received. Here's the fixed version: https://jsfiddle.net/69z2wepo/13008/. Just remember that `this.props` does not change until `componentWillReceiveProps` has finished executing. – Michael Parker Jul 29 '15 at 15:20
3

The setState method is asynchronous, so the new state is not there yet on console.log call. You can pass a callback as a second parameter to setState and call console.log there. In this case the value will be correct.

const314
  • 1,620
  • 11
  • 16
1

Probably because the console.log is triggered before the new state has been really set.

You should use the function:

componentDidUpdate: function() {
    console.log(this.state.number);
}

This function is triggered each time the state is updated.

Hope it helps

Marcus
  • 21
  • 7
François Richard
  • 6,817
  • 10
  • 43
  • 78
0

In the docs of the setState function is an interesting note:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

https://facebook.github.io/react/docs/component-api.html

Wikunia
  • 1,564
  • 1
  • 16
  • 37
0

In order to print state number in console use

this.setState({
        number: num
    },function(){console.log("but wrong here (previous number): " + this.state.number)})

instead of

this.setState({
        number: num
    })
    console.log("but wrong here (previous number): " + this.state.number)

In short: use setState(function|object nextState[, function callback])

bpavlov
  • 1,080
  • 12
  • 32