Arrays in State
Arrays are reference types and so you cannot change their state, you must treat them as immutable. Create a new copy of the array, modify it, and set that as the new state.
| avoid (mutates the array) | prefer (returns a new array) | |
|---|---|---|
| adding | push, unshift | concat, [...arr] spread syntax (example) |
| removing | pop, shift, splice | filter, slice (example) |
| replacing | splice, arr[i] = ... assignment | map (example) |
| sorting | reverse, sort | copy the array first (example) |
Add
setArtists( // Replace the state
[ // with a new array
...artists, // that contains all the old items
{ id: nextId++, name: name } // and one new item at the end
]
);
Remove
setArtists(
artists.filter(a => a.id !== artist.id)
);Replacing
const nextCounters = counters.map((c, i) => {
if (i === index) {
return c + 1;
} else {
return c;
}
});Insert
const insertAt = 1; // Could be any index
const nextArtists = [
// Items before the insertion point:
...artists.slice(0, insertAt),
// New item:
{ id: nextId++, name: name },
// Items after the insertion point:
...artists.slice(insertAt)
];
setArtists(nextArtists);
setName('');Sorting & Mutation
const nextList = [...list];
nextList.reverse();
setList(nextList);
Objects within arrays
You want to create new nested objects
setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// Create a *new* object with changes
return { ...artwork, seen: nextSeen };
} else {
// No changes
return artwork;
}
}));Objects with immer
import { useImmer } from 'use-immer';
const [myTodos, updateMyTodos] = useImmer(initialList);
updateMyTodos(draft => {
const artwork = draft.find(a => a.id === artworkId);
artwork.seen = nextSeen;
});