Home > Mobile >  React - Updating one state object updates another one
React - Updating one state object updates another one

Time:01-10

I'm fetching an object from a database, and creating 2 React state variables from it. One is supposed to remain unchanged, while the user can modify the other. When the user hits the submit button, it is supposed to compare the two objects to find differences. However, when I update one object, the other one is inexplicably updated as well, and I can't for the life of me figure out why.

My variable declarations (newPlanogram should change, and machinePlanogram should stay the same)

  const [machinePlanogram, setMachinePlanogram] = useState({})
  const [newPlanogram, setNewPlanogram] = useState({})

Where I am getting the data and assigning the objects (yes, I am just setting them both to the first document in the list)

  async function findPlanogram() {
    machines.map(async (item, index) => {
      if (item.docID == machineID) {
        setCurrentMachine(item)
        let querySnapshot = await getDocs(collection(db, "Machines", machineID, "planogram"))
        let pdata = []
        querySnapshot.forEach((doc) => {
            pdata.push(doc.data())
        })
        return Promise.resolve(pdata).then(() => {
          let x = pdata[0]
          setNewPlanogram(x)
          setMachinePlanogram(x)
          setWaiting(false)
        })
      }
    })
  }

The UI element where I update values (the first Select object changes the productCode, and the second Select changes maxStock)

        <>
          <Grid item xs={1.66} xl={1.66}>
            <FormControl variant="standard" align="center" size="small" sx={"margin-bottom: 10px"}>
              <Select
                labelId="simple-select-label"
                id="simple-select"
                defaultValue={object.productCode}
                onChange={(e) => updatePlanogramProduct(e, index)}
              >
                {products.map((entry, pndex) => (
                  <MenuItem value={entry.bud}>
                    <img style={imageStyle} src={entry.url}/>
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
            <FormControl variant="standard" align="center" sx={"margin-left: 10px"}>
              <Select
                labelId="demo-simple-select-label"
                id="demo-simple-select"
                defaultValue={object.maxStock}
                onChange={(e) => updatePlanogramQuantity(e, index)}
              >
                <MenuItem value={0}>0</MenuItem>
                <MenuItem value={1}>1</MenuItem>
                <MenuItem value={2}>2</MenuItem>
                <MenuItem value={3}>3</MenuItem>
                <MenuItem value={4}>4</MenuItem>
                <MenuItem value={5}>5</MenuItem>
              </Select>
            </FormControl>
          </Grid>
        </>

And finally the function that updates the newPlanogram object. Although when I use console logs in these functions, it appears as if both newPlanogram and machinePlanogram both update before the function even fires (might be my misunderstanding of async-related things)

  function updatePlanogramProduct (event, index) {
    let y = newPlanogram
    y.planogram.shelves[index].productCode = event.target.value
    setNewPlanogram(y)
  }

  function updatePlanogramQuantity (event, index) {
    let y = newPlanogram
    y.planogram.shelves[index].maxStock = event.target.value
    setNewPlanogram(y)
  }

I know there are far better ways for me to approach some of the things I do in this code, but I'm looking for more of an explanation for why this behavior is occurring rather than code to solve it. Thanks in advance for any help.

CodePudding user response:

In JavaScript, objects are a reference type. So, you need to save a copy of the object not just a refrence..

You can deep clone your object like this:

let clonedPlanogram = JSON.parse(JSON.stringify(x))
// And then 
setNewPlanogram(clonedPlanogram)
  • Related