Reputation: 1348
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
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
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