Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React multiple callbacks not updating the local state

I have a child component called First which is implemented below:

function First(props) {

  const handleButtonClick = () => {
    props.positiveCallback({key: 'positive', value: 'pos'})
    props.negativeCallback({key: 'negative', value: '-100'})
  }

  return (
   <div><button onClick={() => handleButtonClick()}>FIRST</button></div>
  )
}

And I have App.js component.

function App() {

  const [counter, setCounter] = useState({positive: '+', negative: '-'})

  const handleCounterCallback = (obj) => {

      console.log(obj)

      let newCounter = {...counter}
      newCounter[obj.key] = obj.value

      setCounter(newCounter)
  }

  const handleDisplayClick = () => {
    console.log(counter)
  }

  return (
    <div className="App">
      <First positiveCallback = {handleCounterCallback} negativeCallback = {handleCounterCallback} />
      <Second negativeCallback = {handleCounterCallback} />
      <button onClick={() => handleDisplayClick()}>Display</button>
    </div>
  );
}

When handleButtonClick is clicked in First component it triggers multiple callbacks but only the last callback updates the state.

In the example:

 props.positiveCallback({key: 'positive', value: 'pos'}) // not updated
 props.negativeCallback({key: 'negative', value: '-100'}) // updated 

Any ideas?

like image 360
john doe Avatar asked May 30 '26 01:05

john doe


2 Answers

Both are updating the state, your problem is the last one is overwriting the first when you spread the previous state (which isn't updated by the time your accessing it, so you are spreading the initial state). An easy workaround is to split counter into smaller pieces and update them individually

const [positive, setPositive] = useState('+')
const [negative, setNegative] = useState('-')

//This prevents your current code of breaking when accessing counter[key]
const counter = { positive, negative }

const handleCounterCallback = ({ key, value }) => {
   key === 'positive' ? setPositive(value) : setNegative(value)
}
like image 186
Dupocas Avatar answered Jun 01 '26 13:06

Dupocas


You can do that but useState setter is async like this.setState. If you want to base on the previous value you should use setter as function and you can store it in one state - change handleCounterCallback to

const handleCounterCallback = ({key,value}) => {
  setCounter(prev=>({...prev, [key]: value}))
}

and that is all. Always if you want to base on the previous state use setter for the state as function.

I recommend you to use another hook rather than useState which is useReducer - I think it will be better for you

like image 40
Mateusz Krzyżanowski Avatar answered Jun 01 '26 15:06

Mateusz Krzyżanowski