Why setting one state will affact another state?

Viewed 48

So my code looks like this, where I have two same states state1 and state2. Clicking on the "change" button will set the state of state1, but in fact, the value of both states are changed, showing by clicking on the "Show" button. What is causing this issue?

import React, { useEffect, useState } from "react";

interface Obj {
  num: number
}

const App = () => {

  const [state1, setState1] = useState<Array<Obj> | null>(null)
  const [state2, setState2] = useState<Array<Obj> | null>(null)

  useEffect(() => {
    const a = [{num: 1} as Obj]
    setState1(a)
    setState2(a)
  }, [])

  return (
    <div>
      <button
        onClick={() => {
          if (state1 == null) return null
          const temp = [...state1]
          temp[0].num = 100;
          setState1(temp)
        }}  
      >
        Change
      </button>
      <button 
        onClick={() => {
          console.log(state1, '11');
          console.log(state2, '22');
        }}
      >
        Show
      </button>
    </div>
  )
}

export default App
1 Answers

This is because JavaScript objects are passed by reference. Since your object:

const a = [{num: 1} as Obj];

is passed to both state setters, both will reference the same object in memory essentially. So when you later update one of them by setting obj.num = 100, the change will affect both state arrays since they contain the same object. Using the array spread syntax in your temp variable only creates a shallow copy, the object inside is still the same. To ensure this doesn't happen, you can deep clone the object before passing it to the second state setter. For example:

useEffect(() => {
  const a = [{num: 1} as Obj]
  setState1(a)
  // used map() and destructuring syntax here just to clone
  setState2(a.map((obj) => {
    const clone = {...obj, num: 100 }
    return clone
  })))
}, [])

Note that object spread syntax also creates only a shallow copy. If the objects in the array contain references to other objects you will have to clone those too using the same approach.

Related