Skip to content
Advertisement

How to handle concurrent update of multiple states in React Hooks?

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.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement