1

My react state is changing without me calling my setState function from useState hook.

After some quick research, i've narrowed it down to the fact an array is a reference type so data and tempData share the same reference and change with each other. A solution I found was to stringify then parse the data: JSON.parse(JSON.stringify(data)) but this could have some pretty bad performance hits if it's a large object right? Is there a better solution here? Redux? Or is that unnecessary? This is a pretty common case isn't it?

For anyone who cares, this works too but is kinda ugly:
change state to object rather than array

const defaultData = {
  data: [
    {id:0, foo:1, bar:2},
    {id:1, foo:3, bar:4},
    {id:2, foo:4, bar:6},
  ] 
}
const handleData = (id) => {
    setData((prevState) => {
      return {data: data.data.map((i) => i.id === id ? {...i, id:i.id+10} : {...i})}
    })
  }

I've attached an example below which can be easily created from create-react-app.

App.js

import Child from './Child';
import { useState } from 'react';

const defaultData = [
  {id:0, foo:1, bar:2},
  {id:1, foo:3, bar:4},
  {id:2, foo:4, bar:6},
]

function App() {
  const [data, setData] = useState(defaultData)

  const handleData = (id) => {
    const tempData = data
    for (const idx in tempData) {
      const item = tempData[idx]
      if (item.id === id) {
        tempData[idx].id += 10
      }
    }
  }

  return (
    <div>
      <Child data={data} handleData={handleData} />
    </div>
  );
}

export default App;

Child.js

export default function Child(props) {
  const {data, handleData} = props
  
  return (
    <div>
      
      <ul>
      {data.map((i) => (
        <li key={i.id}>
          <button onClick={() => handleData(i.id)}>
            {i.foo} {i.bar}
          </button>
        </li>
      ))}
      </ul>
    </div>
  )
}
cap
  • 337
  • 3
  • 14
  • Mutation of state is forbidden in React, so yes, you will need to create a new object. You don't need to clone *everything* though, only the parts that change – CertainPerformance Mar 08 '22 at 22:54
  • Thats not my question, my point was, state is being updated without me setting the state. I know I can set the state to the temp object I created, but that seems unnecessary as the state would have already changed since I edited the reference – cap Mar 08 '22 at 22:56
  • You are current mutating the state, which can result in many unexpected behaviors. Don't mutate the state, and instead create a new object in place where you need to make changes. – CertainPerformance Mar 08 '22 at 22:58
  • Did you read my post? I said I know I can create a new object by stringifying and parsing – cap Mar 08 '22 at 23:01
  • Which, as you also said, you shouldn't, which is right - instead, only update the parts that need to be changed, not every single nested value – CertainPerformance Mar 08 '22 at 23:02
  • Ok, how would I only update parts that need to be changed? – cap Mar 08 '22 at 23:04

0 Answers0