Utilizarea Immutable.JS cu Redux#

Cuprins#

  • De ce ar trebui să folosesc o bibliotecă axată pe imuabilitate, cum ar fi Immutable.JS?
  • De ce ar trebui să aleg Immutable.JS ca bibliotecă imuabilă?
  • Care sunt problemele legate de utilizarea Immutable.JS?
  • Merită Immutable.JS efortul depus?
  • Care sunt unele dintre cele mai bune practici pentru utilizarea Immutable.JS cu Redux?

De ce ar trebui să folosesc o bibliotecă axată pe imuabilitate, cum ar fi Immutable.JS?#

Bibliotecile axate pe imuabilitate, cum ar fi Immutable.JS au fost concepute pentru a depăși problemele legate de imutabilitate inerente în JavaScript, oferind toate avantajele imutabilității cu performanța de care aplicația dvs. are nevoie.

Dacă alegeți să folosiți o astfel de bibliotecă sau să rămâneți la JavaScript simplu, depinde de cât de confortabil vă simțiți cu adăugarea unei alte dependențe în aplicația dvs. sau cât de sigur sunteți că puteți evita capcanele inerente în cadrul abordării imutabilității de către JavaScript.

Chiar dacă alegeți o astfel de opțiune, asigurați-vă că sunteți familiarizat cu conceptele de imutabilitate, efecte secundare și mutație. În special, asigurați-vă că aveți o înțelegere profundă a ceea ce face JavaScript atunci când actualizează și copiază valori, pentru a vă proteja împotriva mutațiilor accidentale care vor degrada performanța aplicației dvs. sau o vor strica cu totul.

Informații suplimentare#

Documentație

  • Rețete: imutabilitate, efecte secundare și mutație

Articole

  • Introducere la Immutable.js și concepte de programare funcțională
  • Pro și contra utilizării imutabilității cu React.js

De ce ar trebui să aleg Immutable.JS ca bibliotecă imuabilă?#

Immutable.JS a fost conceput pentru a oferi imutabilitate într-un mod performant, într-un efort de a depăși limitările imutabilității cu JavaScript. Principalele sale avantaje includ:

Imutabilitate garantată#

Datele încapsulate într-un obiect Immutable.JS nu sunt niciodată mutate. O nouă copie este întotdeauna returnată. Acest lucru contrastează cu JavaScript, în care unele operații nu mutează datele (de exemplu, unele metode Array, inclusiv map, filter, concat, forEach etc.), dar altele o fac (pop, push, splice, etc.).

API bogat#

Immutable.JS oferă un set bogat de obiecte imuabile pentru a încapsula datele (de ex.ex. hărți, liste, seturi, înregistrări etc.) și un set extins de metode pentru a le manipula, inclusiv metode de sortare, filtrare și grupare a datelor, de inversare a acestora, de aplatizare și de creare de subseturi.

Performanță#

Immutable.JS face multă muncă în spatele scenei pentru a optimiza performanța. Aceasta este cheia puterii sale, deoarece utilizarea structurilor de date imuabile poate implica o mulțime de copieri costisitoare. În special, manipularea imuabilă a unor seturi de date mari și complexe, cum ar fi un arbore de stare Redux imbricate, poate genera multe copii intermediare ale obiectelor, care consumă memorie și încetinesc performanța pe măsură ce colectorul de gunoi al browserului se luptă să curețe lucrurile.

Immutable.JS evită acest lucru prin partajarea inteligentă a structurilor de date sub suprafață, minimizând nevoia de a copia datele. De asemenea, permite realizarea unor lanțuri complexe de operații fără a crea date intermediare clonate inutile (și costisitoare) care vor fi aruncate rapid.

Nu vedeți niciodată acest lucru, bineînțeles – datele pe care le dați unui obiect Immutable.JS nu sunt niciodată mutate. Mai degrabă, datele intermediare generate în cadrul Immutable.JS dintr-o secvență înlănțuită de apeluri de metode sunt cele care pot fi mutate. Prin urmare, beneficiați de toate avantajele structurilor de date imuabile fără niciuna (sau foarte puține) dintre potențialele lovituri de performanță.

Informații suplimentare#

Articole

  • Immutable.js, structuri de date persistente și partajare structurală
  • PDF: JavaScript Immutability – Don’t go changing

Biblioteci

  • Immutable.js

Care sunt problemele legate de utilizarea Immutable.JS?#

Deși puternic, Immutable.JS trebuie utilizat cu atenție, deoarece vine cu probleme proprii. Rețineți, totuși, că toate aceste probleme pot fi depășite destul de ușor cu o codare atentă.

Dificil de interoperat cu#

JavaScript nu oferă structuri de date imuabile. Ca atare, pentru ca Immutable.JS să ofere garanțiile sale de imuabilitate, datele dvs. trebuie să fie încapsulate în cadrul unui obiect Immutable.JS (cum ar fi un Map sau un List, etc.). Odată ce sunt înglobate în acest mod, este greu ca acele date să poată interopera apoi cu alte obiecte JavaScript simple.

De exemplu, nu veți mai putea face referire la proprietățile unui obiect prin notația standard JavaScript cu puncte sau paranteze. În schimb, trebuie să le referiți prin intermediul metodelor get() sau getIn() din Immutable.JS, care utilizează o sintaxă ciudată care accesează proprietățile prin intermediul unui array de șiruri de caractere, fiecare dintre acestea reprezentând o cheie de proprietate.

De exemplu, în loc de myObj.prop1.prop2.prop3, veți utiliza myImmutableMap.getIn().

Acest lucru face dificilă interoperabilitatea nu doar cu propriul cod, ci și cu alte biblioteci, cum ar fi lodash sau ramda, care așteaptă obiecte JavaScript simple.

Rețineți că Immutable.JS au o metodă toJS(), care returnează datele ca o structură de date JavaScript simplă, dar această metodă este extrem de lentă, iar utilizarea ei pe scară largă va anula beneficiile de performanță pe care Immutable.JS le oferă

Odată folosit, Immutable.JS se va răspândi în toată baza dvs. de cod#

După ce vă încapsulați datele cu Immutable.JS, trebuie să folosiți accesorii de proprietate get() sau getIn() ai Immutable.JS pentru a le accesa.

Acest lucru are ca efect răspândirea Immutable.JS în întreaga dvs. bază de cod, inclusiv, potențial, în componentele dvs., unde ați putea prefera să nu aveți astfel de dependențe externe. Întreaga dvs. bază de cod trebuie să știe ce este, și ce nu este, un obiect Immutable.JS. De asemenea, acest lucru face ca eliminarea Immutable.JS din aplicația dvs. să fie dificilă în viitor, în cazul în care veți avea vreodată nevoie.

Această problemă poate fi evitată prin decuplarea logicii aplicației dvs. de structurile de date, așa cum se subliniază în secțiunea de bune practici de mai jos.

Fără operatori de destructurare sau de împrăștiere#

Pentru că trebuie să vă accesați datele prin Immutable.JS și get() și getIn(), nu mai puteți utiliza operatorul de destructurare JavaScript (sau operatorul de răspândire a obiectelor propus), ceea ce face codul dvs. mai verbos.

Nu este potrivit pentru valori mici care se schimbă des#

Immutable.JS funcționează cel mai bine pentru colecții de date, iar cu cât sunt mai mari cu atât mai bine. Poate fi lent atunci când datele dvs. cuprind o mulțime de obiecte JavaScript mici și simple, fiecare dintre ele cuprinzând câteva chei de valori primitive.

Rețineți, totuși, că acest lucru nu se aplică arborelui de stare Redux, care este (de obicei) reprezentat ca o colecție mare de date.

Dificil de depanat#

Obiectele Immmutable.JS, cum ar fi Map, List, etc., pot fi dificil de depanat, deoarece inspectarea unui astfel de obiect va dezvălui o întreagă ierarhie imbricata de proprietăți specifice Immutable.JS de care nu vă interesează, în timp ce datele reale de care vă interesează sunt încapsulate la mai multe straturi de adâncime.

Pentru a rezolva această problemă, utilizați o extensie de browser, cum ar fi Immutable.js Object Formatter, care scoate la suprafață datele dvs. în Chrome Dev Tools și ascunde proprietățile Immutable.JS atunci când inspectați datele.

Întrerupe referințele la obiecte, cauzând performanțe slabe#

Unul dintre avantajele cheie ale imutabilității este că permite verificarea superficială a egalității, ceea ce îmbunătățește dramatic performanța.

Dacă două variabile diferite fac referire la același obiect imuabil, atunci o simplă verificare simplă a egalității celor două variabile este suficientă pentru a determina că sunt egale și că obiectul la care ambele fac referire este neschimbat. Verificarea egalității nu trebuie să verifice niciodată valorile niciuneia dintre proprietățile obiectului, deoarece acesta este, desigur, imuabil.

Cu toate acestea, verificarea superficială nu va funcționa dacă datele dvs. încapsulate într-un obiect Immutable.JS sunt ele însele un obiect. Acest lucru se datorează faptului că metoda toJS() a Immutable.JS, care returnează datele conținute într-un obiect Immutable.JS ca o valoare JavaScript, va crea un nou obiect de fiecare dată când este apelată, și astfel va rupe referința cu datele încapsulate.

În consecință, apelarea toJS() de două ori, de exemplu, și atribuirea rezultatului la două variabile diferite va face ca o verificare a egalității pe aceste două variabile să eșueze, chiar dacă valorile obiectului în sine nu s-au schimbat.

Aceasta este o problemă deosebită dacă utilizați toJS() în funcția mapStateToProps a unei componente înfășurate, deoarece React-Redux compară superficial fiecare valoare din obiectul props returnat. De exemplu, valoarea la care face referire recuzita todos returnată de mapStateToProps de mai jos va fi întotdeauna un obiect diferit și, prin urmare, nu va reuși o verificare superficială a egalității.

// EVITAȚI .toJS() în mapStateToProps
function mapStateToProps(state) {
return {
todos: state.get(‘todos’).toJS() // Întotdeauna un obiect nou
}
}

Copy

Când verificarea superficială eșuează, React-Redux va face ca componenta să fie redată din nou. Prin urmare, utilizarea toJS() în mapStateToProps în acest mod va face ca întotdeauna componenta să fie redată din nou, chiar dacă valoarea nu se schimbă niciodată, având un impact puternic asupra performanței.

Acest lucru poate fi prevenit prin utilizarea toJS() într-o componentă de ordin superior, așa cum se discută în secțiunea Cele mai bune practici de mai jos.

Informații suplimentare#

Articole

  • Immutabile.js, structuri de date persistente și partajare structurală
  • Structuri de date imuabile și JavaScript
  • React.js pure render performance anti-pattern
  • Building Efficient UI with React and Redux

Chrome Extension

  • Immutable Object Formatter

Merită efortul de a folosi Immutable.JS?#

Frecvent, da. Există diverse compromisuri și opinii de luat în considerare, dar există multe motive bune pentru a utiliza Immutable.JS. Nu subestimați dificultatea de a încerca să depistați o proprietate a arborelui de stare care a fost mutată din greșeală.

Componentele se vor reda atât atunci când nu ar trebui, cât și vor refuza să se redea atunci când ar trebui, iar depistarea bug-ului care cauzează problema de redare este dificilă, deoarece componenta care se redă incorect nu este neapărat cea ale cărei proprietăți sunt mutate din greșeală.

Această problemă este cauzată predominant de returnarea unui obiect de stare mutat de la un reductor Redux. Cu Immutable.JS, această problemă pur și simplu nu există, eliminând astfel o întreagă clasă de erori din aplicația dumneavoastră.

Acesta, împreună cu performanța sa și API-ul bogat pentru manipularea datelor, este motivul pentru care Immutable.JS merită efortul.

Informații suplimentare#

Documentație

  • Soluționarea problemelor: Nu se întâmplă nimic când expediez o acțiune

Care sunt câteva bune practici de opinie pentru utilizarea Immutable.JS cu Redux?#

Immutable.JS poate oferi îmbunătățiri semnificative ale fiabilității și performanței aplicației dumneavoastră, dar trebuie să fie utilizat corect. Dacă alegeți să utilizați Immutable.JS (și nu uitați, nu sunteți obligat să o faceți și există și alte biblioteci imuabile pe care le puteți utiliza), urmați aceste cele mai bune practici opinate și veți putea să profitați la maximum de el, fără să vă împiedicați de problemele pe care le poate cauza potențial.

Nu amestecați niciodată obiecte JavaScript simple cu Immutable.JS#

Nu lăsați niciodată un obiect JavaScript simplu să conțină proprietăți Immutable.JS. În egală măsură, nu lăsați niciodată un obiect Immutable.JS să conțină un obiect JavaScript simplu.

Informații suplimentare#

Articole

  • Structuri de date imuabile și JavaScript

Faceți din întregul arbore de stare Redux un obiect Immutable.JS object#

Pentru o aplicație Redux, întregul arbore de stări ar trebui să fie un obiect Immutable.JS, fără a se folosi deloc obiecte JavaScript simple.

  • Creați arborele folosind funcția fromJS() din Immutable.JS.

  • Utilizați un obiect Immutable.JS-aware a funcției combineReducers, cum ar fi cea din redux-immutable, deoarece Redux însuși se așteaptă ca arborele de stare să fie un obiect JavaScript simplu.

  • Când adăugați obiecte JavaScript la o hartă sau o listă Immutable.JS folosind Immutable.JS folosind metodele update, merge sau set, asigurați-vă că obiectul care se adaugă este mai întâi convertit într-un obiect Immutable folosind fromJS().

Exemplu

// evitați
const newObj = { key: value }
const newState = state.setIn(, newObj)
// newObj a fost adăugat ca un obiect JavaScript simplu, NU ca o hartă Immutable.JS
// recomandat
const newObj = { key: value }
const newState = state.setIn(, fromJS(newObj))
// newObj este acum o hartă Immutable.JS

Copy

Informații suplimentare#

Articole

  • Structuri de date imuabile și JavaScript

Biblioteci

  • redux-imuabile

Utilizați Immutable.JS peste tot, cu excepția componentelor voastre stupide#

Utilizarea Immutable.JS peste tot menține codul vostru performant. Folosiți-l în componentele dvs. inteligente, în selectori, în sagas sau thunks, în creatorii de acțiuni și, mai ales, în reductoare.

Nu folosiți însă Immutable.JS în componentele dvs. stupide.

Informații suplimentare#

Articole

  • Immutable Data Structures and JavaScript
  • Smart and Dumb Components in React

Limitați utilizarea toJS()#

toJS() este o funcție costisitoare și anulează scopul utilizării Immutable.JS. Evitați utilizarea sa.

Informații suplimentare#

Discuții

  • Lee Byron pe Twitter: „Sfat perfect pentru #immutablejs…”

Selectorii dvs. ar trebui să returneze obiecte Immutable.JS#

Întotdeauna. Această practică are mai multe avantaje:

  • Evită redistribuirile inutile cauzate de apelarea .toJS() în selectori (deoarece .toJS() va returna întotdeauna un obiect nou).
    • Este posibil să memorați selectorii în care apelați .toJS(), dar este redundant atunci când este suficient să returnați doar obiecte Immutable.js fără memoizare.
  • Se stabilește o interfață coerentă pentru selectori; nu va trebui să urmăriți dacă va fi returnat un obiect Immutable.js sau un obiect JavaScript simplu.

Utilizați obiecte Immutable.JS în componentele dvs. inteligente#

Componentele inteligente care accesează magazinul prin intermediul funcției connect din React Redux trebuie să utilizeze valorile Immutable.JS returnate de către selectorii dvs. Asigurați-vă că evitați problemele potențiale pe care acest lucru le poate cauza prin redarea inutilă a componentelor. Memorați-vă selectorii folosind o bibliotecă precum reselect, dacă este necesar.

Informații suplimentare#

Documentație

  • Rețete: Calcularea datelor derivate
  • FAQ: Date imuabile
  • Documentație de reeselecție: Cum folosesc Reselect cu Immutable.js?

Articole

  • Redux Patterns and Anti-Patterns

Librării

  • Reselect: Selector library for Redux

Nu folosiți niciodată toJS() în mapStateToProps#

Convertirea unui obiect Immutable.JS într-un obiect JavaScript folosind toJS() va returna de fiecare dată un obiect nou. Dacă faceți acest lucru în mapStateToProps, veți determina componenta să creadă că obiectul s-a schimbat de fiecare dată când se modifică arborele de stări și astfel veți declanșa o redare inutilă.

Informații suplimentare#

Documentație

  • FAQ: Immutable Data

Nu folosiți niciodată Immutable.JS în componentele dvs. stupide#

Componentele dvs. stupide ar trebui să fie pure; adică, ar trebui să producă aceeași ieșire având aceeași intrare și să nu aibă dependențe externe. Dacă treceți unei astfel de componente un obiect Immutable.JS ca prop, o faceți dependentă de Immutable.JS pentru a extrage valoarea prop-ului și pentru a o manipula în alt mod.

O astfel de dependență face componenta impură, îngreunează testarea componentei și face reutilizarea și refactorizarea componentei inutil de dificilă.

Informații suplimentare#

Articole

  • Structuri de date imuabile și JavaScript
  • Componente inteligente și proaste în React
  • Tips pentru o arhitectură Redux mai bună: Lessons for Enterprise Scale

Folosiți o componentă de ordin superior pentru a converti propulsoarele Immutable.JS ale componentei dvs. inteligente în propulsoarele JavaScript ale componentei Dumb#

Ceva trebuie să mapeze propulsoarele Immutable.JS din componenta dvs. inteligentă în propulsoarele pur JavaScript utilizate în componenta Dumb. Acel ceva este o componentă de ordin superior (Higher Order Component – HOC) care preia pur și simplu propulsoarele Immutable.JS din componenta dvs. inteligentă și le convertește folosind toJS() în propulsoare JavaScript pure, care sunt apoi transmise componentei dvs. proaste.

Un exemplu de astfel de HOC urmează. Un HOC similar este disponibil ca un pachet NPM pentru confortul dumneavoastră: with-immutable-props-to-js.

import React from ‘react’
import { Iterable } from ‘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

Și iată cum îl veți folosi în componenta dvs. inteligentă:

import { connect } from ‘react-redux’

import { toJS } from ‘./to-js’
import DumbComponent from ‘./dumb.component’
const mapStateToProps = state => {
return {
// obj este un obiect Immutable în Smart Component, dar este convertit într-un simplu
// obiect JavaScript de către toJS, și astfel este trecut la DumbComponent ca un obiect pur JavaScript
//. Pentru că este încă un obiect Immutable.JS aici în mapStateToProps, totuși,
// nu există nicio problemă cu redarea eronată.
obj: getImmutableObjectFromStateTree(state)
} }
}
export default connect(mapStateToProps)(toJS(DumbComponent))

Copy

Prin convertirea obiectelor Immutable.JS în valori JavaScript simple în cadrul unui HOC, obținem portabilitatea componentei Dumb Component, dar fără a suferi de performanța pe care o are utilizarea toJS() în componenta inteligentă.

Nota: dacă aplicația dvs. necesită o performanță ridicată, este posibil să trebuiască să evitați toJS() cu totul și astfel va trebui să utilizați Immutable.JS în componentele dvs. Dumb Component. Cu toate acestea, pentru majoritatea aplicațiilor, acest lucru nu va fi cazul, iar beneficiile de a păstra Immutable.JS în afara componentelor dvs. stupide (mentenabilitate, portabilitate și testare mai ușoară) vor depăși cu mult orice îmbunătățiri de performanță percepute dacă îl păstrați în componente.

În plus, utilizarea toJS într-o componentă de ordin superior nu ar trebui să provoace o degradare a performanțelor prea mare, dacă nu chiar deloc, deoarece componenta va fi apelată doar atunci când se schimbă elementele de recuzită ale componentei conectate. Ca în cazul oricărei probleme de performanță, efectuați mai întâi verificări de performanță înainte de a decide ce să optimizați.

Informații suplimentare#

Documentație

  • React: Componente de ordin superior

Articole

  • React Componente de ordin superior în profunzime

Discuții

  • Reddit: acemarke și cpsubrian comentează despre Dan Abramov: Redux nu este o arhitectură sau un model de proiectare, este doar o bibliotecă.

Gists

  • cpsubrian: Decoratori React pentru componente „inteligente” redux/react-router/immutable

Folosiți extensia Chrome Immutable Object Formatter pentru a ajuta la depanare#

Instalați Immutable Object Formatter , și inspectați datele Immutable.JS fără să vedeți zgomotul proprietăților propriilor obiecte Immutable.JS.

Lasă un răspuns

Adresa ta de email nu va fi publicată.