React suggests not to mutate state. I have an array of objects which I am manipulating based on some events. My question is, is it okay to write it like this:
const makeCopy = (arr) => arr.map((item) => ({ ...item }));
function SomeComponenet() {
const [filters, setFilters] = useState(aemFilterData);
const handleFilterClick = (filter, c) => {
let copiedFilters = makeCopy(filters);
/**
* Apply toggle on the parent as well
*/
if (!("parentId" in filter)) {
copiedFilters[filter.id].open = !copiedFilters[filter.id].open;
}
setFilters(copiedFilters);
}
}
Am I mutating the original object by doing like above? Or does it make a difference if written like this:
const makeCopy = (arr) => arr.map((item) => ({ ...item }));
function SomeComponent() {
const [filters, setFilters] = useState(aemFilterData);
const handleFilterClick = (filter, c) => {
let copiedFilters = makeCopy(filters);
/**
* Apply toggle on the parent as well
*/
if (!("parentId" in filter)) {
copiedFilters = copiedFilters.map((f) => {
if (filter.id === f.id) {
return {
...f,
open: !f.open,
};
} else {
return { ...f };
}
});
}
setFilters(copiedFilters);
}
}
What's the preferred way to do this? Spread operators are getting a lot verbose and I am not liking it, but I prefer it if that's how I need to do it here. immutable.js and immer or not an option right now.
CodePudding user response:
const makeCopy = (arr) => arr.map((item) => item );
With above code, it's mutating on the original object reference because we're not creating a deep clone.
copiedFilters[filter.id].open = !copiedFilters[filter.id].open;
Here reference of copiedFilters[filter.id]
and filters[filter.id]
is same.
With spread operator
const makeCopy = (arr) => arr.map((item) => ({ ...item }));
Here we create a new copy of the inner object too. So copiedFilters[filter.id]
and filters[filter.id]
will have different reference.
This is same as your second approach.
So either you use spread operator while making a copy or you can skip making a copy in the second approach and directly map on filters
since you're using spread operator there. This looks better because, why run loop twice - first to create copy and then to update open
.
// let copiedFilters = makeCopy(filters); Not needed in second approach
copiedFilters = copiedFilters.map((f) => {
if (filter.id === f.id) {
return {
...f,
open: !f.open,
};
} else {
return { ...f };
}
});
You can create a deep clone when you copy but that would be waste of computation and memory, I don't think it's needed here. Deep clone is helpful when you have further nesting in the object.