I have an array of objects as a state variable. I am trying to write a function that changes a field in one of the objects.
interface IItem{
id: string,
selected: boolean
}
const [array, setArray] = useState<IItem[]>([])
const toggleItem = (id : string) => {
const item = array.find(i => i.id === id)
if (item) {
const copy = {...item, selected: !!item.selected}
setArray(array.map(i => i.id === id ? copy : i))
}
}
The code works, but it looks too complex for this. Is there a way to simplify? (Note, that the order of the items in the array matters).
If you want to simplify state changes, I would recommend checking out the package Immer. With Immer you get a writeable draft of the state that you can mutate. The code might be as long, be if you're working with even more deeply nested object it is very helpful. I also think in you case it helps a lot with readability.
import { useState } from "react";
import { produce } from "immer";
interface IItem {
id: string;
selected: boolean;
}
const [array, setArray] = useState<IItem[]>([]);
const toggleItem = (id: string) => {
setArray(
produce((draft) => {
const item = draft.find((el) => el.id === id);
item.selected = !item.selected;
}),
);
};
EDIT
If you don't want to introduce a package for local state, I don't think there's an obvious way to change your code to make it less complex. Here's an alternative at least which I would say is a bit more easy to read.
interface IItem {
id: string;
selected: boolean;
}
const [array, setArray] = useState<IItem[]>([]);
const toggleItem = (id: string) => {
setArray((previous) =>
previous.map((item) => {
if (item.id === id) {
return {
...item,
selected: true,
};
}
return item;
}),
);
};