Search code examples
reactjsreact-hooksfrontend

How setstate function works in react


Simple example: i want to choose one card from the deck and take it to my hand. When i choose the card, i append it to state "chosenCard". So, when i chose the card, i want to do next thing:

setMyCards(prevState => [...prevState, {...chosenCard}])
setChosenCard(null)

The question is: is it okay? Changing state is asyncronous, so is it accurate, that first function will work properly before second one? And if yes, why? Is it possible, that second function can change the state and first function will work incorrect (maybe in more complicated case)?


Solution

  • If I understand you correctly, you are worried that the second line of code could take effect before the first one, effectively adding a null value to myCards.

    You can safely assume, that this will not happen. When setMyCards is executed, chosenCard will not be reset to null yet. Reading the value of chosenCard in the first line is not asynchronous, so anything that happens later in the code will not have any effect.

    You could have a different situation if you had the opposite execution order:

    setChosenCard({ newValue: 42 })
    setMyCards(prevState => [...prevState, {...chosenCard}])
    

    In this case, the new value of chosenCard would become visible in the next render cycle only and setMyCards would not pick it up yet.

    There are different ways to deal with this:

    don't immediately read a state variable after setting its value

    const newValue = { newValue: 42 }
    setChosenCard(newValue)
    setMyCards(prevState => [...prevState, newValue])
    

    use useReducer

    if your application often modifies several state variables that have mutual dependencies, it could be better to use useReducer instead of useState. It would allow you to define a single action, which if dispatched, simultaneously updates chosenCard and myCards in the application state

    manage state dependencies with useEffect

    useEffect(
      ()=> chosenCard !== null && setMyCards(prevState => [...prevState, {...chosenCard}]),
      [chosenCard]
    )
    

    This will ensure that setMyCards is called only after the state change have taken effect. It is however discouraged to excessively use useEffect because it will make it hard to reason about implicit state changes: you-might-not-need-an-effect