In the code below, I’ve handled the concurrent change of multiple state variables by using a unique global “State”, but I don’t think it is the best way to do so.
Can anybody suggest me how to change multiple states without keeping them together as I did?
Here’s the working code with the “complex state”
import { useState } from 'react' const App = () => { const [state, setState] = useState({ good: 0, neutral: 0, bad: 0, tot: 0, weights: 0, avg: 0, posPercent: 0 }); const handleGood = () => { setState({ ...state, good: state.good +1, tot: state.tot +1, weights: (state.good+1)*1 + state.neutral*0 + state.bad*(-1), avg: ((state.good+1)*1 + state.neutral*0 + state.bad*(-1))/(state.tot +1), posPercent: ((state.good+1)*100)/(state.tot+1) }); } const handleNeutral = () => { setState({ ...state, neutral: state.neutral +1, tot: state.tot +1, weights: state.good*1 + (state.neutral+1)*0 + state.bad*(-1), avg: (state.good*1 + (state.neutral+1)*0 + state.bad*(-1))/(state.tot +1), posPercent: ((state.good)*100)/(state.tot+1) }); } const handleBad = () => { setState({ ...state, bad: state.bad +1, tot: state.tot +1, weights: state.good*1 + state.neutral*0 + (state.bad+1)*(-1), avg: (state.good*1 + state.neutral*0 + (state.bad+1)*(-1))/(state.tot +1), posPercent: ((state.good)*100)/(state.tot+1) }); } return ( <div> <h1>give feedback</h1> <button onClick={handleGood}> good </button> <button onClick={handleNeutral}> neutral </button> <button onClick={handleBad}> bad </button> <h1>statistics</h1> <p>good {state.good}</p> <p>neutral {state.neutral}</p> <p>bad {state.bad}</p> <p>all {state.tot}</p> <p>average {state.avg}</p> <p>positive {state.posPercent} %</p> </div> ) } export default App
Advertisement
Answer
useMemo
, please
The biggest issue I see here (looking at your 2nd piece of code), is that you’re manually trying to update values that are calculated (namely, posPercent
, avg
, tot
)
That’s certainly doable, but it’s a lot more headache than you probably want.
useMemo
re-calculates a value whenever one of the given dependencies changes:
const total = useMemo(() => good + neutral + bad), [good, neutral, bad]);
With this in place for all three calculated values, you’re only responsible for updating the good, neutral, bad counts.
Functional updates
Note how you can use functional updates
to make your handlers very streamlined:
// … this could/should be defined outside of the component const increment = (x) => x + 1; // Then in your component: const handleGood = setGood(increment) const handleBad = setGood(increment) // …
This is merely a stylistic choice, setGood(good + 1)
works just as well. I like it because increment
is so nicely readable.
and a bit of math
I honestly didn’t get any deeper into what you’re trying to calculate. neutral*0
though seems, well, a bit redundant. If my math doesn’t fail me here, you could just leave this out.