I am trying to compare state against a previous version of the same object (useRef) to detect for changes.
The object that loads into state with useEffect looks like this:
{
active: true,
design: [
{
existing: '55Xgm53McFg1Bzr3qZha',
id: '38fca',
options: ['John Smith', 'Jane Doe'],
required: true,
title: 'Select your name',
type: 'Selection List'
},
{
existing: '55Xgm53McFg1Bzr3qZha',
id: '38fca',
options: ['John Smith', 'Jane Doe'],
required: true,
title: 'Select your name',
type: 'Selection List'
},
],
icon: '🚜',
id: '92yIIZxdFYoWk3FMM0vI',
projects: false,
status: true,
title: 'Prestarts'
}
This is how I load in my app object and define a comparitor state (previousApp) to compare it and detect changes (with lodash) this all works great until I change a value in the design array.
const [app, setApp] = useState(null)
const [edited, setEdited] = useState(false)
const previousApp = useRef(null)
useEffect(() => {
if (app === null) {
getApp(someAppId).then(setApp).catch(window.alert)
}
if (previousApp.current === null && app) {
previousApp.current = { ...app }
}
if (previousApp.current && app) {
_.isEqual(app, previousApp.current) ? setEdited(false) : setEdited(true)
}
}, [app])
For example I change the input value of app.design[0].title = 'testing' with the following code:
const updateItem = e => {
let newApp = { ...app }
let { name, value } = e.target
newApp.design[0][name] = value
setApp(newApp)
}
This works in the sense that it updates my app state object but not that it detects any changes in comparison to previousApp.current
When I console log app and previousApp after changing both values, they are identical. Its seems to update my previousApp value aswell for some reason.
Rearranging the design array works as expected and detects a change with the following function:
const updateDesign = e => {
let newApp = { ...app }
newApp.design = e
setApp(newApp)
}
It probably wasn't detecting any difference because the array present in the design
property was keeping the same reference.
When you do this:
let newApp = { ...app }
You're creating a new object for your app, but the property values of that object will remain the same, because it's a shallow copy. Thus, the design
array (which is an object, and therefore is handled by reference) will keep its value (the reference) unaltered.
I think this would also solve your problem:
const updateItem = e => {
let newApp = { ...app };
let newDesign = Array.from(app.design); // THIS CREATES A NEW ARRAY REFERENCE FOR THE 'design' PROPERTY
let { name, value } = e.target;
newDesign[0][name] = value;
setApp({
...newApp,
design: newDesign
});
}