Since you want to maintain immutability when setting your state within React (so, always make a new object or clone), and since the spread ... operator doesn't clone deeply, you should make use of structuredClone().
Take a look at the MDN:
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
The global structuredClone() method creates a deep clone of a given value using the structured clone algorithm.
The method also allows transferable objects in the original value to be transferred rather than cloned to the new object. Transferred objects are detached from the original object and attached to the new object; they are no longer accessible in the original object.
This is more or less the modern replacement (as of this post at least) for the JSON.parse(JSON.stringify(yourObject)) method of cloning.
Here's a functional component example:
const nestedAnimals = {
lists: ['Dogs', 'Cats'],
items: {
Dogs: [
{name: "Snoopy"},
{name: "Lola"},
{name: "Sprinkles"}
],
Cats: [
{name: "Felidae"},
{name: "Garfiled"},
{name: "Cat in the Hat"}
]
}
}
const someComponent = (props) => {
const [animals, setAnimals] = useState(nestedAnimals)
const [selectedList, setSelectedList] = useState(animals.lists[0])
const [animalInput, setAnimalInput] = useState("")
// Handler for adding a new animal to our deeply nested object.
function addAnimal(event) {
const newAnimal = {name: animalInput}
// Here's where the deep cloning magic happens.
const newAnimals = structuredClone(animals)
newAnimals.items[selectedList].push(newAnimal)
// Now you've updated your state in an immutable fashion.
setAnimals(newAnimals)
}
// Handler for swapping lists.
function changeList(event) {
setSelectedList(event.target.value)
}
// Handler for storing the value from the input field as it changes.
function changeAnimalInput(event) {
setAnimalInput(event.target.value)
}
return (
<div>
<input type="text" value={animalInput} onChange={changeAnimalInput}/>
<button onClick={addAnimal}>Add Animal</button>
<select onChange={changeList}>
{animals.lists.map(list => <option value={list}>{list}</option>)}
</select>
<ul>
{animals.items[selectedList].map(animal => <li>{animal.name}</li>)}
</ul>
</div>
)
}
this.state = { dogs: [], cats: [] }. Might make life a little easier.