Amir
Amir

Reputation: 1348

One step delay in updating result in React State

I have a very simple calculator that calculates two inputs by React and I have a problem for updating result in my state (update result state by a function called calc). This is done with the next action and not in the moment!

In summary, now result state calculates a wrong value.

My Code:

const App = () => {
  const [state, setState] = useState({
    firstVal: 0,
    secondVal: 0,
    operator: "+",
    result: 0
  });

  const { firstVal, secondVal, operator, result } = state;

  const calc = (firstVal, secondVal, operator) => {
    if (operator === "+") {
      return firstVal + secondVal;
    } else if (operator === "-") {
      return firstVal - secondVal;
    } else if (operator === "*") {
      return firstVal * secondVal;
    } else if (operator === "/") {
      return firstVal / secondVal;
    }
  };

  const changeHandler = e => {
    setState({
      ...state,
      [e.target.name]:
        e.target.name === "operator" ? e.target.value : Number(e.target.value),
      result: calc(firstVal, secondVal, operator)
    });
  };

  return (
    <div className="App">
      <input name="firstVal" onInput={changeHandler} />

      <select name="operator" onChange={changeHandler}>
        <option value="+">+</option>
        <option value="-">-</option>
        <option value="*">*</option>
        <option value="/">/</option>
      </select>

      <input name="secondVal" onInput={changeHandler} />

      <p>{result}</p>
    </div>
  );
};

export default App;

I think I don't understood something about asynchronous and state in React.

Here is my code in codesandbox

Upvotes: 0

Views: 2362

Answers (2)

Tom Oakley
Tom Oakley

Reputation: 6403

Instead of holding result in your state, just calculate the result using the calc method at render time.

  return (
    <div className="App">
      <input name="firstVal" onInput={changeHandler} />

      <select name="operator" onChange={changeHandler}>
        <option value="+">+</option>
        <option value="-">-</option>
        <option value="*">*</option>
        <option value="/">/</option>
      </select>

      <input name="secondVal" onInput={changeHandler} />

      <p>{calc(firstVal, secondVal, operator)}</p>
    </div>
  );

I'd also not use a state object - use different state values and methods for each state variable, for example:

import React, { useState } from "react";
import "./styles.css";

const App = () => {

  const [firstVal, setFirstVal] = useState(0)
  const [secondVal, setSecondVal] = useState(0)
  const [operator, setOperator] = useState('+')

  const calc = (firstVal, secondVal, operator) => {
    if (operator === "+") {
      return firstVal + secondVal;
    } else if (operator === "-") {
      return firstVal - secondVal;
    } else if (operator === "*") {
      return firstVal * secondVal;
    } else if (operator === "/") {
      return firstVal / secondVal;
    }
  };

  return (
    <div className="App">
      <input name="firstVal" onInput={e => setFirstVal(Number(e.target.value))} />

      <select name="operator" onChange={e => setOperator(e.target.value)}>
        <option value="+">+</option>
        <option value="-">-</option>
        <option value="*">*</option>
        <option value="/">/</option>
      </select>

      <input name="secondVal" onInput={e => setSecondVal(Number(e.target.value))} />

      <p>{calc(firstVal, secondVal, operator)}</p>
    </div>
  );
};

export default App;

This removes the complicated mess of the changeHandler method and makes the code far more readable.

Upvotes: 5

Addono
Addono

Reputation: 148

The issue is in:

const changeHandler = e => {
    setState({
      ...state,
      [e.target.name]:
        e.target.name === "operator" ? e.target.value : Number(e.target.value),
      result: calc(firstVal, secondVal, operator)
    });
  };

When calc is invoked, it still has the values from the current state, not the new value which has been set through the onChange/onInput handler, hence the observed stale results.

Edit: This answer tries to explain why the observed behavior happened. With regard to how to solve it, see Tom's answer.

Upvotes: 3

Related Questions