Usare Immutable.JS con Redux#

Indice#

  • Perché dovrei usare una libreria immutabile come Immutable.JS?
  • Perché dovrei scegliere Immutable.JS come libreria immutabile?
  • Quali sono i problemi nell’usare Immutable.JS?
  • Vale la pena usare Immutable.JS?
  • Quali sono alcune Best Practices per usare Immutable.JS con Redux?

Perché dovrei usare una libreria immutabile come Immutable.JS?

Le librerie immutabili come Immutable.JS sono state progettate per superare i problemi di immutabilità insiti in JavaScript, fornendo tutti i benefici dell’immutabilità con le prestazioni che la tua app richiede.

Se scegliete di usare una libreria di questo tipo, o di attenervi al semplice JavaScript, dipende da quanto siete a vostro agio con l’aggiunta di un’altra dipendenza alla vostra applicazione, o quanto siete sicuri di poter evitare le insidie inerenti all’approccio di JavaScript all’immutabilità.

Qualunque opzione scegliate, assicuratevi di avere familiarità con i concetti di immutabilità, effetti collaterali e mutazione. In particolare, assicurati di avere una profonda comprensione di ciò che JavaScript fa quando aggiorna e copia i valori, al fine di proteggerti da mutazioni accidentali che degradano le prestazioni della tua app, o la rompono del tutto.

Ulteriori informazioni#

Documentazione

  • Ricette: immutabilità, effetti collaterali e mutazione

Articoli

  • Introduzione a Immutable.js e concetti di programmazione funzionale
  • Pro e contro dell’uso dell’immutabilità con React.js

Perché dovrei scegliere Immutable.JS come libreria immutabile?

Immutable.JS è stato progettato per fornire immutabilità in modo performante nel tentativo di superare le limitazioni dell’immutabilità con JavaScript. I suoi principali vantaggi includono:

Immutabilità garantita#

I dati incapsulati in un oggetto Immutable.JS non vengono mai mutati. Viene sempre restituita una nuova copia. Questo contrasta con JavaScript, in cui alcune operazioni non mutano i tuoi dati (ad esempio alcuni metodi Array, inclusi map, filter, concat, forEach, ecc.), ma altri sì (Array’s pop, push, splice, ecc.).

Rich API#

Immutable.JS fornisce un ricco set di oggetti immutabili per incapsulare i tuoi dati (es.), e un ampio set di metodi per manipolarli, inclusi metodi per ordinare, filtrare e raggruppare i dati, invertirli, appiattirli e creare sottoinsiemi.

Performance#

Immutable.JS fa un sacco di lavoro dietro le quinte per ottimizzare le prestazioni. Questa è la chiave della sua potenza, poiché l’uso di strutture di dati immutabili può comportare un sacco di costose copie. In particolare, la manipolazione immutabile di grandi e complessi insiemi di dati, come un albero di stato Redux annidato, può generare molte copie intermedie di oggetti, che consumano memoria e rallentano le prestazioni mentre il garbage collector del browser combatte per pulire le cose.

Immutable.JS evita questo condividendo abilmente strutture di dati sotto la superficie, minimizzando la necessità di copiare i dati. Permette anche di eseguire complesse catene di operazioni senza creare inutili (e costosi) dati intermedi clonati che verranno rapidamente gettati via.

Non lo vedrete mai, naturalmente – i dati che date a un oggetto Immutable.JS non vengono mai mutati. Piuttosto, sono i dati intermedi generati all’interno di Immutable.JS da una sequenza concatenata di chiamate a metodi che sono liberi di essere mutati. Si ottengono quindi tutti i benefici delle strutture dati immutabili con nessuno (o molto poco) dei potenziali problemi di performance.

Ulteriori informazioni#

Articoli

  • Immutable.js, strutture dati persistenti e condivisione strutturale
  • PDF: JavaScript Immutability – Don’t go changing

Libraries

  • Immutable.js

Quali sono i problemi con l’uso di Immutable.JS?

Anche se potente, Immutable.JS deve essere usato con attenzione, poiché ha i suoi problemi. Si noti, comunque, che tutti questi problemi possono essere superati abbastanza facilmente con un’attenta codifica.

Difficile interoperare con#

JavaScript non fornisce strutture dati immutabili. Come tale, affinché Immutable.JS fornisca le sue garanzie di immutabilità, i tuoi dati devono essere incapsulati all’interno di un oggetto Immutable.JS (come un Map o un List, ecc.). Una volta che sono contenuti in questo modo, è difficile per quei dati interoperare con altri oggetti JavaScript semplici.

Per esempio, non sarete più in grado di fare riferimento alle proprietà di un oggetto attraverso la notazione standard JavaScript dei punti o delle parentesi. Invece, dovrete fare riferimento ad esse attraverso i metodi get() o getIn() di Immutable.JS, che usano una sintassi imbarazzante che accede alle proprietà attraverso un array di stringhe, ognuna delle quali rappresenta una chiave di proprietà.

Per esempio, invece di myObj.prop1.prop2.prop3, userete myImmutableMap.getIn().

Questo rende imbarazzante l’interoperabilità non solo con il proprio codice, ma anche con altre librerie, come lodash o ramda, che si aspettano semplici oggetti JavaScript.

Nota che gli oggetti Immutable.JS hanno un metodo toJS(), che restituisce i dati come una semplice struttura dati JavaScript, ma questo metodo è estremamente lento, e usarlo estensivamente annullerà i benefici di performance che Immutable.JS fornisce

Una volta usato, Immutable.JS si diffonderà in tutta la vostra codebase#

Una volta incapsulati i vostri dati con Immutable.JS, dovete usare gli accessi alle proprietà get() o getIn() di Immutable.JS per accedervi.

Questo ha l’effetto di diffondere Immutable.JS in tutto il vostro codebase, inclusi potenzialmente i vostri componenti, dove potreste preferire non avere tali dipendenze esterne. La vostra intera codebase deve sapere cosa è, e cosa non è, un oggetto Immutable.JS. Questo rende anche difficile rimuovere Immutable.JS dalla tua applicazione in futuro, se mai dovessi averne bisogno.

Questo problema può essere evitato disaccoppiando la tua logica applicativa dalle tue strutture dati, come delineato nella sezione delle migliori pratiche qui sotto.

Nessuna destrutturazione o Operatori Spread#

Perché devi accedere ai tuoi dati attraverso il sistema Immutable.JS i propri metodi get() e getIn(), non potete più usare l’operatore di destrutturazione di JavaScript (o l’operatore di diffusione dell’oggetto proposto), rendendo il vostro codice più prolisso.

Non adatto a piccoli valori che cambiano spesso#

Immutable.JS funziona meglio per collezioni di dati, e più grandi sono meglio è. Può essere lento quando i vostri dati comprendono molti piccoli e semplici oggetti JavaScript, ognuno dei quali comprende poche chiavi di valori primitivi.

Nota, comunque, che questo non si applica all’albero di stato Redux, che è (di solito) rappresentato come una grande collezione di dati.

Difficile da debuggare#

Gli oggetti Immutable.JS, come Map, List, ecc, possono essere difficili da debuggare, poiché ispezionando un tale oggetto si rivelerà un’intera gerarchia annidata di proprietà specifiche di Immutable.JS di cui non ci si preoccupa, mentre i dati effettivi di cui ci si preoccupa sono incapsulati a diversi livelli di profondità.

Per risolvere questo problema, usate un’estensione del browser come Immutable.js Object Formatter, che fa emergere i vostri dati in Chrome Dev Tools, e nasconde le proprietà di Immutable.JS quando ispezionate i vostri dati.

Rompe i riferimenti agli oggetti, causando scarse prestazioni#

Uno dei vantaggi chiave dell’immutabilità è che permette un controllo di uguaglianza superficiale, che migliora drasticamente le prestazioni.

Se due variabili diverse fanno riferimento allo stesso oggetto immutabile, allora un semplice controllo di uguaglianza delle due variabili è sufficiente per determinare che sono uguali, e che l’oggetto a cui entrambe fanno riferimento non è cambiato. Il controllo di uguaglianza non ha mai bisogno di controllare i valori di qualsiasi proprietà dell’oggetto, poiché esso è, ovviamente, immutabile.

Tuttavia, il controllo superficiale non funzionerà se i vostri dati incapsulati in un oggetto Immutable.JS sono essi stessi un oggetto. Questo perché il metodo toJS() di Immutable.JS, che restituisce i dati contenuti in un oggetto Immutable.JS come valore JavaScript, creerà un nuovo oggetto ogni volta che viene chiamato, e quindi romperà il riferimento con i dati incapsulati.

Di conseguenza, chiamare toJS() due volte, per esempio, e assegnare il risultato a due variabili diverse causerà il fallimento di un controllo di uguaglianza su queste due variabili, anche se i valori degli oggetti stessi non sono cambiati.

Questo è un problema particolare se si usa toJS() nella funzione mapStateToProps di un componente wrapped, poiché React-Redux confronta superficialmente ogni valore nell’oggetto props restituito. Per esempio, il valore a cui fa riferimento la prop todos restituita da mapStateToProps di seguito sarà sempre un oggetto diverso, e quindi fallirà un controllo di uguaglianza superficiale.

// EVITA .toJS() in mapStateToProps
function mapStateToProps(state) {
return {
todos: state.get(‘todos’).toJS() // Sempre un nuovo oggetto
}
}

Copy

Quando il controllo superficiale fallisce, React-Redux causerà il re-rendering del componente. Usare toJS() in mapStateToProps in questo modo, quindi, causerà sempre un re-rendering del componente, anche se il valore non cambia mai, impattando pesantemente sulle prestazioni.

Questo può essere evitato usando toJS() in un componente di ordine superiore, come discusso nella sezione Best Practices sotto.

Ulteriori informazioni#

Articoli

  • Immutabili.js, strutture dati persistenti e condivisione strutturale
  • Strutture dati immutabili e JavaScript
  • React.js puro rendering anti-pattern
  • Costruire UI efficienti con React e Redux

Estensione Chrome

  • Immutable Object Formatter

Usare Immutable.JS vale lo sforzo?

Spesso sì. Ci sono vari compromessi e opinioni da considerare, ma ci sono molte buone ragioni per usare Immutable.JS. Non sottovalutare la difficoltà di cercare di rintracciare una proprietà del tuo albero di stato che è stata inavvertitamente mutata.

I componenti ri-renderanno quando non dovrebbero, e si rifiuteranno di rendere quando dovrebbero, e rintracciare il bug che causa il problema di rendering è difficile, poiché il componente che rende in modo errato non è necessariamente quello le cui proprietà sono state accidentalmente mutate.

Questo problema è causato principalmente dal ritorno di un oggetto di stato mutato da un riduttore Redux. Con Immutable.JS, questo problema semplicemente non esiste, rimuovendo così un’intera classe di bug dalla tua app.

Questo, insieme alle sue prestazioni e alla sua ricca API per la manipolazione dei dati, è il motivo per cui Immutable.JS vale lo sforzo.

Ulteriori informazioni#

Documentazione

  • Correzione dei problemi: Non succede niente quando eseguo un’azione

Quali sono alcune Best Practice per usare Immutable.JS con Redux?

Immutable.JS può fornire significativi miglioramenti di affidabilità e prestazioni alla tua app, ma deve essere usato correttamente. Se scegliete di usare Immutable.JS (e ricordate, non è obbligatorio, e ci sono altre librerie immutabili che potete usare), seguite queste opinionate best practices, e sarete in grado di ottenere il massimo da esso, senza inciampare in nessuno dei problemi che può potenzialmente causare.

Mai mescolare oggetti JavaScript semplici con Immutable.JS#

Non lasciate mai che un oggetto JavaScript semplice contenga proprietà Immutable.JS. Allo stesso modo, non lasciare mai che un oggetto Immutable.JS contenga un semplice oggetto JavaScript.

Ulteriori informazioni#

Articoli

  • Strutture di dati immutabili e JavaScript

Fai del tuo intero albero di stato Redux un oggetto Immutable.JS#

Per un’applicazione Redux, il tuo intero albero di stato dovrebbe essere un oggetto Immutable.JS, senza utilizzare alcun oggetto JavaScript puro.

  • Crea l’albero usando la funzione fromJS() di Immutable.JS.

  • Usa una versione Immutable.JS-aware della funzione combineReducers, come quella in redux-immutable, poiché Redux stesso si aspetta che l’albero di stato sia un semplice oggetto JavaScript.

  • Quando si aggiungono oggetti JavaScript a una mappa o lista Immutable.JS usando la funzione Immutable.JS update, merge o set, assicuratevi che l’oggetto aggiunto sia prima convertito in un oggetto Immutable usando fromJS().

Esempio

// evitare
const newObj = { key: value }
const newState = state.setIn(, newObj)
// newObj è stato aggiunto come un semplice oggetto JavaScript, NON come una Immutable.JS Map
// consigliato
const newObj = { key: value }
const newState = state.setIn(, fromJS(newObj))
// newObj è ora una mappa Immutable.JS

Copia

Ulteriori informazioni#

Articoli

  • Strutture dati immutabili e JavaScript

Librerie

  • redux-immutable

Usa Immutable.JS ovunque tranne che nei tuoi componenti stupidi#

Usare Immutable.JS ovunque mantiene il tuo codice performante. Usalo nei tuoi componenti intelligenti, nei tuoi selettori, nelle tue saghe o thunks, nei creatori di azioni e specialmente nei tuoi riduttori.

Non usare, comunque, Immutable.JS nei tuoi componenti stupidi.

Altre informazioni#

Articoli

  • Strutture di dati immutabili e JavaScript
  • Componenti intelligenti e stupidi in React

Limitare l’uso di toJS()#

toJS() è una funzione costosa e annulla lo scopo di usare Immutable.JS. Evita il suo uso.

Ulteriori informazioni#

Discussioni

  • Lee Byron su Twitter: “Perf tip for #immutablejs…”

I tuoi selettori dovrebbero restituire oggetti Immutable.JS#

Sempre. Questa pratica ha diversi vantaggi:

  • Evita inutili ritorni causati dalla chiamata di .toJS() nei selettori (poiché .toJS() restituirà sempre un nuovo oggetto).
    • È possibile memorizzare i selettori dove si chiama .toJS(), ma è ridondante quando è sufficiente restituire oggetti Immutable.js senza memorizzare.
  • Stabilisce un’interfaccia coerente per i selettori; non dovrai tenere traccia se verrà restituito un oggetto Immutable.js o un semplice oggetto JavaScript.

Usa gli oggetti Immutable.JS nei tuoi Smart Components#

I componenti intelligenti che accedono al negozio tramite la funzione connect di React Redux devono usare i valori Immutable.JS restituiti dai tuoi selettori. Assicuratevi di evitare i potenziali problemi che questo può causare con il re-rendering non necessario dei componenti. Memorizza i tuoi selettori usando una libreria come reselect se necessario.

Ulteriori informazioni#

Documentazione

  • Ricette: Calcolo dei dati derivati
  • FAQ: Dati Immutabili
  • Documentazione su Riselezione: Come uso Reselect con Immutable.js?

Articoli

  • Redux Patterns e Anti-Patterns

Libraries

  • Reselect: Selector library for Redux

Mai usare toJS() in mapStateToProps#

Convertire un oggetto Immutable.JS in un oggetto JavaScript usando toJS() restituirà un nuovo oggetto ogni volta. Se lo fai in mapStateToProps, farai credere al componente che l’oggetto sia cambiato ogni volta che l’albero degli stati cambia, e quindi scatenerai un inutile re-rendering.

Ulteriori informazioni#

Documentazione

  • FAQ: Immutable Data

Non usare mai Immutable.JS nei tuoi Dumb Components#

I tuoi dumb components dovrebbero essere puri; cioè, dovrebbero produrre lo stesso output dato lo stesso input, e non avere dipendenze esterne. Se passate ad un tale componente un oggetto Immutable.JS come prop, lo rendete dipendente da Immutable.JS per estrarre il valore del prop e altrimenti manipolarlo.

Tale dipendenza rende il componente impuro, rende il test del componente più difficile, e rende il riutilizzo e il refactoring del componente inutilmente difficile.

Altre informazioni#

Articoli

  • Strutture dati immutabili e JavaScript
  • Componenti intelligenti e stupidi in React
  • Consigli per una migliore architettura Redux: Lessons for Enterprise Scale

Usa un Higher Order Component per convertire gli oggetti di scena Immutable.JS del tuo Smart Component in oggetti di scena JavaScript del tuo Dumb Component#

Qualcosa deve mappare gli oggetti di scena Immutable.JS nel tuo Smart Component agli oggetti di scena JavaScript puri usati nel tuo Dumb Component. Quel qualcosa è un Higher Order Component (HOC) che semplicemente prende gli oggetti di scena Immutable.JS dal vostro Smart Component, e li converte usando toJS() in oggetti di scena JavaScript puri, che vengono poi passati al vostro Dumb Component.

Segue un esempio di un tale HOC. Un HOC simile è disponibile come pacchetto NPM per comodità: with-immutable-props-to-js.

import React da ‘react’
import { Iterable } da ‘immutable’
export const toJS = WrappedComponent => wrappedComponentProps => {
const KEY = 0
const VALUE = 1
const propsJS = Object.entries(wrappedComponentProps).reduce(
(newProps, wrappedComponentProp) => {
newProps] = Iterable.isIterable(
wrappedComponentProp
)
? wrappedComponentProp.toJS()
: wrappedComponentProp
return newProps
},
{}
)
return <WrappedComponent {…propsJS} />
}

Copy

E questo è come lo useresti nel tuo Smart Component:

import { connect } da ‘react-redux’
import { toJS } da ‘./to-js’
import DumbComponent da ‘./dumb.component’
const mapStateToProps = state => {
return {
// obj è un oggetto Immutable in Smart Component, ma è convertito in un semplice
// oggetto JavaScript da toJS, e quindi passato a DumbComponent come un puro oggetto JavaScript
//. Poiché è ancora un oggetto Immutable.JS qui in mapStateToProps, però,
// non c’è nessun problema con i re-rendering errati.
obj: getImmutableObjectFromStateTree(state)
}
}
export default connect(mapStateToProps)(toJS(DumbComponent))

Copy

Convertendo gli oggetti Immutable.JS in semplici valori JavaScript all’interno di un HOC, otteniamo la portabilità del Dumb Component, ma senza gli effetti sulle prestazioni dell’uso di toJS() nello Smart Component.

Nota: se la tua app richiede alte prestazioni, potresti dover evitare del tutto toJS() e quindi dovrai usare Immutable.JS nei tuoi componenti stupidi. Tuttavia, per la maggior parte delle applicazioni questo non sarà il caso, e i benefici di tenere Immutable.JS fuori dai tuoi componenti stupidi (manutenibilità, portabilità e test più facili) supereranno di gran lunga qualsiasi miglioramento delle prestazioni percepito nel tenerlo dentro.

Inoltre, usare toJS in un componente di ordine superiore non dovrebbe causare molta, se non nessuna, degradazione delle prestazioni, poiché il componente sarà chiamato solo quando i puntelli del componente collegato cambiano. Come per qualsiasi problema di prestazioni, conducete prima dei controlli sulle prestazioni prima di decidere cosa ottimizzare.

Ulteriori informazioni#

Documentazione

  • React: Componenti di ordine superiore

Articoli

  • React Componenti di ordine superiore in profondità

Discussioni

  • Reddit: acemarke e cpsubrian commenta Dan Abramov: Redux non è un’architettura o un design pattern, è solo una libreria.

Gists

  • cpsubrian: React decorators for redux/react-router/immutable ‘smart’ components

Use the Immutable Object Formatter Chrome Extension to Aid Debugging#

Installa Immutable Object Formatter , e ispeziona i tuoi dati Immutable.JS senza vedere il rumore delle proprietà degli oggetti Immutable.JS.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.