Ciao a tutti, sto lavorando su un'app React con Redux Toolkit e mi imbatto continuamente in problemi di mutazione dello stato non intenzionale. Nonostante conosca il principio dell'immutabilità, quando devo aggiornare strutture complesse come array di oggetti annidati, finisco per modificare accidentalmente lo stato esistente. Ecco un esempio del mio reducer:
```javascript
const initialState = { users: [{ id: 1, roles: ['admin'] }] };
function userReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_ROLE':
// Tentativo fallito: mutazione diretta!
state.users[0].roles.push('editor');
return { ...state };
default:
return state;
}
}
```
Ricevo l'errore "Cannot assign to read only property 'roles' of object" in modalità strict. Ho provato con lo spread operator (`[...state.users]`), ma quando clono array annidati, perdo l'immutabilità. Ho anche testato `map` e `Object.assign` senza successo duraturo. Qualcuno ha esperienza con aggiornamenti immutabili profondi in Redux? Consigliate librerie come Immer o ci sono pattern nativi puliti per oggetti complessi? Grazie per qualsiasi spunto!
Ah, l'immutabilità in Redux... un classico rompicapo! Vedo benissimo il problema: nel tuo codice fai una *shallow copy* con `...state`, ma gli oggetti annidati come `users[0]` e `roles` rimangono riferimenti allo stato originale. Ecco perché push() rompe tutto.
Prova così con l'approccio nativo, più sicuro:
```javascript
case 'ADD_ROLE':
return {
...state,
users: state.users.map(user =>
user.id === 1
? { ...user, roles: [...user.roles, 'editor'] }
: user
)
};
```
Cloni ogni livello: lo stato → l'array users → l'oggetto user → l'array roles. Funziona, ma per strutture complesse diventa un incubo di spread operator annidati.
Onestamente? Per casi come questo, **Immer è una manna**. Con Redux Toolkit è già integrato in `createReducer`/`createSlice`: scrivi logica "mutabile" e lui genera lo stato immutabile per te. Altro che vintage, è il futuro che semplifica il passato! Se vuoi evitare dipendenze, preparati a battaglie con spread a 5 livelli... ma davvero, concediti Immer.
Ecco, ti capisco perfettamente! Anche io ho sudato sangue con sta roba prima di capire la soluzione. Il problema è che stai modificando l'array roles direttamente, e Redux ti punisce giustamente perché l'immutabilità è sacra.
La risposta di @rebeccamartini è già ottima, ma voglio aggiungere due cose:
1. **Approccio nativo** (senza librerie):
```javascript
case 'ADD_ROLE':
return {
...state,
users: state.users.map(user => ({
...user,
roles: user.id === 1 ? [...user.roles, 'editor'] : user.roles
}))
};
```
Sì, è verboso, ma almeno hai il controllo totale. Però se aggiungi altri livelli annidati, diventa ingestibile.
2. **La salvezza con Immer**:
Se usi Redux Toolkit (e dovresti!), usa `createSlice` e lascia che Immer gestisca l'immutabilità per te:
```javascript
const slice = createSlice({
name: 'users',
initialState,
reducers: {
addRole(state) {
state.users[0].roles.push('editor'); // Sembra una mutazione, ma non lo è!
}
}
});
```
Immer fa il lavoro sporco al posto tuo, e il codice resta pulito.
Personalmente, odio sprecare tempo con spread infiniti, quindi uso sempre Immer. Se vuoi essere purista, preparati a scrivere codice più lungo, ma almeno dormi sereno sapendo che lo stato è immutabile!
P.S.: Se vedi qualcuno usare `Object.assign` per questi casi, mandalo qui che gli spiego perché è 2024, non 2015. 😅
Oh, @erindesantis, che gioia leggere qualcuno che capisce l'estasi di un `push()` senza sensi di colpa! 😂 E sì, il paragone con Nolan è perfetto: quella sensazione di "tutto ha un senso" dopo ore di debugging è la stessa di quando finalmente capisci *Inception*.
Per quanto riguarda i tuoi consigli notturni: le *selector functions* di Immer sono davvero una manna! Mi hanno salvato la vita con un progetto dove dovevo gestire strutture annidate che sembravano uscite da un incubo lovecraftiano. E quelle 3AM passate a maledire gli spread operator? Oh, le conosco bene... ormai le chiamo "terapia d'urto per sviluppatori".
Un piccolo suggerimento extra: se usi TypeScript, abbinato a Immer diventa ancora più potente. La combinazione ti evita di impazzire con i tipi in strutture complesse. E se proprio vuoi ottimizzare, dai un'occhiata a `reselect` per le selector più complesse.
P.S. Videocassette? Ma per favore, siamo nel 2024! 😤 (Oppure no, forse è solo nostalgia... ma solo per i film, non per `Object.assign`!)