DEV Community

Kalap
Kalap

Posted on

This is why your React state might not be updating!

Hi guys! Today I'd like to share a simple bug that I have found all the time when I first start learning React😅. Although the state is not updating on re-render, there is nothing related to virtual DOM or react-related concepts here. Instead this is a mistake that you might come across when coding in VanillaJS‼️


Goal

Let say you have a list of numerous items, each item has a button and increment its value when being clicked

My attempts

My state looks like this:

const [Test, setTest] = useState([
    {
      name: 'Eric',
      value: 0
    },
    {
      name: 'Frank',
      value: 20
    },
    // and more....
  ])
Enter fullscreen mode Exit fullscreen mode

Rendering part looks like this:

<div className="App">
      {
        Test.map((item, idx) => (
          <button
          key={idx}
            onClick={(e) => {
              e.preventDefault()
              setTest((value) => {
                value.find(element => element.name === item.name).value += 1
                return value
              })
            }}
          >
            {
              item.name + ': ' + item.value
            }
          </button>
        ))
      }
    </div>
Enter fullscreen mode Exit fullscreen mode

Let's see...

Image description

Second attempt (90% of the time I make this mistake😷)

I've learnt that I should use an updater of setState, however it seems I have use it the wrong way🤒 I have mutated the state directly. Let's fix that by creating a new copy of the previous state.

//...
    <button
      key={idx}
      onClick={(e) => {
        e.preventDefault()
        setTest((value) => {
          let temp = value
          temp.find(element => element.name === item.name).value += 1
          return temp
        })
      }}
    >
      {
        item.name + ': ' + item.value
      }
    </button>
//...
Enter fullscreen mode Exit fullscreen mode

My Solution

The code that does the job:

//... 
  setTest((value) => {
    let temp = [...value] 
    temp.find(element => element.name === item.name).value += 1
    return temp
  })
//...
Enter fullscreen mode Exit fullscreen mode

Image description
Spread operator... does help create a copy of the original state so that we don't mutate the original state, but this is just a one level shallow copy, it is still not recommended when dealing with nested object.

In our case, the element of state array is an object, which means the elements are reference, mutating them is still mutating the original state.

Moreover, nested state is not the intended use of React state, if you find yourself making a heavily nested state, you are not using it correctly!🦥

What do you think about it? Do you know any better solution? Did you come up with this problem and how do you solve it? Let me know in the comment!🙈
Cheers!

Top comments (0)