2

Im a noob in React and trying to make a simple app for water phases where the user enters a number and then based on the value it should display the state of water, for example if he enters 212 it should say gas and for 12 it should say solid, but for some reason its not displaying the values correctly, Any help is greatly appreciated!!!

class App extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            msg: "liquid",
            temp: 0
        };
        this.handlenum1Change = this.handlenum1Change.bind(this);
    }

    handlenum1Change(evt) {
        console.log(evt.target.value);
        this.setState({
            temp: Number(evt.target.value)
        });

        let temp = this.state.temp
        if (temp > 100) {
            this.setState({
                msg: "boiling"
            })
        } else if (temp < 32) {
            this.setState({
                msg: "frozen"
            })
        } else {
            this.setState({
                msg: "liquid"
            })
        }
    }

    render() {
        return (
            <div>
                <h1> {this.state.msg} </h1>
                <form className="form-inline">
                    <div className="form-group">
                        <label> Temp: </label>
                        <input type="number"  onChange={this.handlenum1Change} className="form-control" />
                    </div>
                </form>
            </div>
        );
    }
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<div id="root"></div>

<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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Leon Bogod
  • 428
  • 2
  • 10
  • 27
  • Welcome to Stack Overflow! When asking for help, please take the time to format your code with reasonable, consistent indentation and without a huge amount of unnecessary whitespace. (It's a good idea when *not* asking for help, too.) – T.J. Crowder May 23 '18 at 15:48
  • Thanks for including all of the necessary code in the question. As you can see, I updated the question to make a *runnable version* using Stack Snippets (the toolbar button that looks like `<>` in a page). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder May 23 '18 at 15:57

4 Answers4

8

setState is asynchronous and won't update the state straight away. It collects multiple state changes before updating. That means, that this.state won't hold your new value right away.

Or to quote the React docs here:

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.


Instead, do it the other way around and work with the user input before setting the new state. That way you can also collectively set both, the temperature and the message at once:

const temp = Number(evt.target.value);
let msg = '';
if (temp > 100) {
  msg = 'boiling';
} else if (temp < 32) {
  msg = 'frozen';
} else {
  msg = 'liquid';
}

this.setState({
  temp,
  msg,
});
lumio
  • 7,428
  • 4
  • 40
  • 56
1

setState is asynchronous. Separately, if you set state (setState({msg: ...})) based on current state (this.state.temp), you must use the callback version of setState.

But in this case you can just set temp and msg at the same time, since they're both working from something outside of state (the temp from the input):

handlenum1Change(evt) {
    console.log(evt.target.value);
    const temp = Number(evt.target.value);
    let msg;

    if (temp > 100) {
        msg = "boiling";
    } else if (temp < 32) {
        msg = "frozen";
    } else {
        msg = "liquid";
    }
    this.setState({temp, msg});
}

class App extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            msg: "liquid",
            temp: 0
        };
        this.handlenum1Change = this.handlenum1Change.bind(this);
    }

    handlenum1Change(evt) {
        console.log(evt.target.value);
        const temp = Number(evt.target.value);
        let msg;

        if (temp > 100) {
            msg = "boiling";
        } else if (temp < 32) {
            msg = "frozen";
        } else {
            msg = "liquid";
        }
        this.setState({temp, msg});
    }

    render() {
        return (
            <div>
                <h1> {this.state.msg} </h1>
                <form className="form-inline">
                    <div className="form-group">
                        <label> Temp: </label>
                        <input type="number"  onChange={this.handlenum1Change} className="form-control" />
                    </div>
                </form>
            </div>
        );
    }
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<div id="root"></div>

<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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

setState is not an imperative method to change the state immediately but a request you make to React to change the state of your component as soon as it can (see here).

Another important thing you should factor in is that the event React uses is not a standard HTML Event but rather a SyntheticEvent created by React itself to wrap the standard event, and this SyntheticEvent will be discarded as soon as possible for "performance reasons".

Now, your problem is easily solved by changing

const temp = this.state.temp

To

const temp = Number(evt.target.value)

but you should keep in mind the two above factors when using a React Component's state.

Edit: The consideration about SyntethicEvents is especially important when using the callback version of setState, since accessing the value from within that callback will most likely not contain the value of the input that fired the onChange event, so you have to store it in a variable in the scope outside the callback, for example:

handleInputChange = (event) => {
     const value = event.target.value

     this.setState(prevState => {
          return { myValue: prevState.value + value }
     })
}
Gian Marco Toso
  • 11,676
  • 5
  • 29
  • 38
-1

enter image description here enter image description here
enter image description hereSince you are not using this.state.temp anywhere else except comparing, you can assign the value in a variable and compare it. FYI, setState wont reflect the changes immediately.

handlenum1Change(evt) {
        let temp = evt.target.value;
        if (temp > 100) {
            this.setState({
                msg: "boiling"
            })
        } else if (temp < 32) {
            this.setState({
                msg: "frozen"
            })
        } else {
            this.setState({
                msg: "liquid"
            })
        }
    }
Johnson Samuel
  • 2,046
  • 2
  • 18
  • 29