I'm running a bunch of generated checkboxes through useState
as a generic object, eg:
const [stuff, setStuff] = useState(() => {
// do stuff to get saved stuff.
// assume very basic format of { uuid: boolean, uuid: boolean, ... }
return theStuff
}
The stuff is then fed to the UI by child components that uses the stuff, eg:
{stuff.map((thing, i) => (
<Fragment key={i}>
<input
type="checkbox"
id={thing}
name={thing}
checked={stuff[thing] || false}
onChange={() => handleChanges(thing)}
/>
</Fragment>
)};
Now when one of the few hundred checkboxes is created and one/many get checked I just throw the state object mentioned in the first snippet. This works great, updates the state on the screen re-renders and also updates the values and re-renders on other sibling components so I can display like counts of selected etc.
export const handleChanges = (thing) => {
setStuff((prevStuff) => ({
...prevStuff,
[thing]: !prevStuff[thing]
}));
}
The Problem
It may be in handleChanges
I assume my learning curve on React lifecycle handling is hurting me because:
delete stuff.uuid
then it WILL update the values used on the sibling components both ways...but it does not update the checked state of the checkbox.Hope this isn't too verbose, but I can't seem to find how to have setState
update both when adding and removing from the object on the sibling components. This while keeping the state object a nice clean object of only the uuid's selected.
The issue is that you are using the array index as the React key. When you remove an element from the array all the elements after are shuffled up an index, but the index value is still the same index value, so React bails on rerendering the elements because it doesn't think they changed.
You should use React keys that are "sticky" to the element/object they represent. Any unique property will suffice. It seems like your data has "uuid" key values, so this is an excellent candidate for React key usage.
Update the rendering to use the thing
value as the mapped React key.
Here I've used Object.entries
to return an array of key-value pairs for mapping, using the uuid
key as the React key and identifier, and thing
as the checkbox boolean value.
{Object.entries(stuff).map(([uuid, thing]) => (
<input
key={uuid}
type="checkbox"
id={uuid}
name={uuid}
checked={!!thing}
onChange={() => handleChanges(uuid)}
/>
)}