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>
)
}