Verwendung von Immutable.JS mit Redux#

Inhaltsverzeichnis#

  • Warum sollte ich eine auf Unveränderlichkeit fokussierte Bibliothek wie Immutable.JS verwenden?
  • Warum sollte ich Immutable.JS als unveränderliche Bibliothek wählen?
  • Was sind die Probleme bei der Verwendung von Immutable.JS?
  • Ist Immutable.JS den Aufwand wert?
  • Was sind einige anerkannte Best Practices für die Verwendung von Immutable.JS mit Redux?

Warum sollte ich eine auf unveränderliche Bibliotheken wie Immutable.JS verwenden?#

Auf unveränderliche Bibliotheken wie Immutable.JS wurden entwickelt, um die Probleme mit der Unveränderlichkeit in JavaScript zu überwinden und bieten alle Vorteile der Unveränderlichkeit mit der Leistung, die Ihre Anwendung benötigt.

Ob Sie sich für die Verwendung einer solchen Bibliothek entscheiden oder bei einfachem JavaScript bleiben, hängt davon ab, wie wohl Sie sich dabei fühlen, Ihrer Anwendung eine weitere Abhängigkeit hinzuzufügen, oder wie sicher Sie sind, dass Sie die Fallstricke vermeiden können, die dem JavaScript-Ansatz zur Unveränderbarkeit innewohnen.

Für welche Option Sie sich auch entscheiden, stellen Sie sicher, dass Sie mit den Konzepten der Unveränderbarkeit, Seiteneffekte und Mutation vertraut sind. Stellen Sie insbesondere sicher, dass Sie genau verstehen, was JavaScript beim Aktualisieren und Kopieren von Werten tut, um sich vor versehentlichen Mutationen zu schützen, die die Leistung Ihrer Anwendung beeinträchtigen oder sie sogar ganz zerstören.

Weitere Informationen#

Dokumentation

  • Rezepte: Unveränderlichkeit, Seiteneffekte und Mutation

Artikel

  • Einführung in Immutable.js und funktionale Programmierkonzepte
  • Vor- und Nachteile der Verwendung von Unveränderlichkeit mit React.js

Warum sollte ich mich für Immutable.JS als unveränderliche Bibliothek entscheiden?#

Immutable.JS wurde entwickelt, um Unveränderlichkeit in einer performanten Art und Weise zu bieten, um die Einschränkungen der Unveränderlichkeit in JavaScript zu überwinden. Zu den wichtigsten Vorteilen gehören:

Garantierte Unveränderlichkeit#

Daten, die in einem Immutable.JS-Objekt gekapselt sind, werden niemals verändert. Es wird immer eine neue Kopie zurückgegeben. Dies steht im Gegensatz zu JavaScript, bei dem einige Operationen Ihre Daten nicht verändern (z. B. einige Array-Methoden, einschließlich map, filter, concat, forEach usw.), einige jedoch schon (pop, push, splice usw. von Array).

Reichhaltige API#

Immutable.JS bietet eine Vielzahl unveränderlicher Objekte zur Kapselung Ihrer Daten (z.z.B. Maps, Lists, Sets, Records, etc.) und einen umfangreichen Satz von Methoden, um diese zu manipulieren, einschließlich Methoden zum Sortieren, Filtern und Gruppieren der Daten, zum Umkehren, Verflachen und Erstellen von Untergruppen.

Performance#

Immutable.JS macht viel Arbeit hinter den Kulissen, um die Performance zu optimieren. Dies ist der Schlüssel zu seiner Leistung, da die Verwendung unveränderlicher Datenstrukturen eine Menge teurer Kopiervorgänge mit sich bringen kann. Insbesondere die unveränderliche Bearbeitung großer, komplexer Datensätze, wie z. B. eines verschachtelten Redux-Zustandsbaums, kann viele Zwischenkopien von Objekten erzeugen, die Speicher verbrauchen und die Leistung beeinträchtigen, da der Garbage Collector des Browsers mit dem Aufräumen kämpft.

Immutable.JS vermeidet dies, indem es geschickt Datenstrukturen unter der Oberfläche gemeinsam nutzt und so die Notwendigkeit, Daten zu kopieren, minimiert. Es ermöglicht auch die Ausführung komplexer Operationsketten, ohne unnötige (und teure) geklonte Zwischendaten zu erzeugen, die schnell weggeworfen werden.

Das sehen Sie natürlich nie – die Daten, die Sie einem Immutable.JS-Objekt übergeben, werden nie verändert. Vielmehr sind es die Zwischendaten, die innerhalb von Immutable.JS aus einer verketteten Abfolge von Methodenaufrufen erzeugt werden, die frei verändert werden können. Sie erhalten daher alle Vorteile unveränderlicher Datenstrukturen ohne (oder mit nur sehr geringen) Leistungseinbußen.

Weitere Informationen#

Artikel

  • Immutable.js, persistente Datenstrukturen und strukturelle Freigabe
  • PDF: JavaScript Immutability – Don’t go changing

Bibliotheken

  • Immutable.js

Welche Probleme gibt es bei der Verwendung von Immutable.JS?#

Auch wenn Immutable.JS sehr leistungsfähig ist, muss es mit Bedacht eingesetzt werden, da es seine eigenen Probleme mit sich bringt. Beachten Sie jedoch, dass alle diese Probleme bei sorgfältiger Codierung recht einfach überwunden werden können.

Schwierige Interoperabilität#

JavaScript bietet keine unveränderlichen Datenstrukturen. Damit Immutable.JS seine unveränderlichen Garantien bieten kann, müssen Ihre Daten in einem Immutable.JS-Objekt gekapselt sein (z. B. Map oder List usw.). Sobald die Daten auf diese Weise eingeschlossen sind, ist es für sie schwierig, mit anderen einfachen JavaScript-Objekten zu interagieren.

Zum Beispiel können Sie die Eigenschaften eines Objekts nicht mehr über die Standard-JavaScript-Punkt- oder Klammer-Notation referenzieren. Stattdessen müssen Sie sie über die Methoden get() oder getIn() von Immutable.JS referenzieren, die eine umständliche Syntax verwenden, die auf Eigenschaften über ein Array von Strings zugreift, von denen jeder einen Eigenschaftsschlüssel darstellt.

Zum Beispiel würden Sie statt myObj.prop1.prop2.prop3 myImmutableMap.getIn() verwenden.

Das macht es schwierig, nicht nur mit dem eigenen Code zu interagieren, sondern auch mit anderen Bibliotheken wie lodash oder ramda, die einfache JavaScript-Objekte erwarten.

Beachten Sie, dass Immutable.JS-Objekte haben eine toJS()-Methode, die die Daten als einfache JavaScript-Datenstruktur zurückgibt, aber diese Methode ist extrem langsam, und wenn Sie sie ausgiebig verwenden, werden die Leistungsvorteile, die Immutable.JS bietet, zunichte gemacht

Einmal verwendet, wird sich Immutable.JS in Ihrer gesamten Codebasis ausbreiten#

Wenn Sie Ihre Daten mit Immutable.JS gekapselt haben, müssen Sie die get()– oder getIn()-Eigenschaftszugriffsfunktionen von Immutable.JS verwenden, um darauf zuzugreifen.

Dies hat zur Folge, dass Immutable.JS in Ihrer gesamten Codebasis verbreitet wird, möglicherweise auch in Ihren Komponenten, in denen Sie solche externen Abhängigkeiten nicht haben möchten. Ihre gesamte Codebasis muss wissen, was ein Immutable.JS-Objekt ist und was nicht. Das macht es auch schwierig, Immutable.JS in Zukunft aus Ihrer Anwendung zu entfernen, sollten Sie dies jemals benötigen.

Dieses Problem kann vermieden werden, indem Sie Ihre Anwendungslogik von Ihren Datenstrukturen entkoppeln, wie im Abschnitt „Best Practices“ unten beschrieben.

Keine Destrukturierungs- oder Spread-Operatoren#

Da Sie auf Ihre Daten über die Immutable.JS zugreifen müssen, können Sie den JavaScript-Operator für die Destrukturierung (oder den vorgeschlagenen Operator für die Verteilung von Objekten) nicht mehr verwenden, wodurch Ihr Code umfangreicher wird.

Nicht geeignet für kleine Werte, die sich häufig ändern#

Immutable.JS funktioniert am besten für Datensammlungen, und je größer, desto besser. Es kann langsam sein, wenn Ihre Daten aus vielen kleinen, einfachen JavaScript-Objekten bestehen, von denen jedes ein paar Schlüssel mit primitiven Werten enthält.

Beachten Sie jedoch, dass dies nicht für den Redux-Zustandsbaum gilt, der (normalerweise) als große Datensammlung dargestellt wird.

Schwierig zu debuggen#

Immutable.JS-Objekte, wie Map, List, etc, können schwer zu debuggen sein, da die Inspektion eines solchen Objekts eine ganze verschachtelte Hierarchie von Immutable.JS-spezifischen Eigenschaften offenbart, die Sie nicht interessieren, während Ihre eigentlichen Daten, die Sie interessieren, mehrere Schichten tief gekapselt sind.

Um dieses Problem zu lösen, verwenden Sie eine Browsererweiterung wie den Immutable.js Object Formatter, der Ihre Daten in Chrome Dev Tools anzeigt und die Eigenschaften von Immutable.JS ausblendet, wenn Sie Ihre Daten inspizieren.

Bricht Objektreferenzen, was zu schlechter Leistung führt#

Einer der Hauptvorteile der Unveränderlichkeit ist, dass sie eine oberflächliche Gleichheitsprüfung ermöglicht, was die Leistung erheblich verbessert.

Wenn zwei verschiedene Variablen auf dasselbe unveränderliche Objekt verweisen, dann reicht eine einfache Gleichheitsprüfung der beiden Variablen aus, um festzustellen, dass sie gleich sind und dass das Objekt, auf das sie beide verweisen, unverändert ist. Die Gleichheitsprüfung muss niemals die Werte einer der Eigenschaften des Objekts überprüfen, da dieses natürlich unveränderlich ist.

Die oberflächliche Prüfung funktioniert jedoch nicht, wenn die in einem Immutable.JS-Objekt gekapselten Daten selbst ein Objekt sind. Das liegt daran, dass die toJS()-Methode von Immutable.JS, die die in einem Immutable.JS-Objekt enthaltenen Daten als JavaScript-Wert zurückgibt, bei jedem Aufruf ein neues Objekt erzeugt und so die Referenz mit den eingekapselten Daten unterbricht.

Wird toJS() beispielsweise zweimal aufgerufen und das Ergebnis zwei verschiedenen Variablen zugewiesen, schlägt eine Gleichheitsprüfung für diese beiden Variablen fehl, obwohl sich die Objektwerte selbst nicht geändert haben.

Dies ist ein besonderes Problem, wenn Sie toJS() in der mapStateToProps-Funktion einer umhüllten Komponente verwenden, da React-Redux jeden Wert im zurückgegebenen props-Objekt oberflächlich vergleicht. Zum Beispiel wird der Wert, der von der todos Requisite referenziert wird, die von der mapStateToProps Funktion zurückgegeben wird, immer ein anderes Objekt sein und somit eine oberflächliche Gleichheitsüberprüfung nicht bestehen.

// AVOID .toJS() in mapStateToProps
function mapStateToProps(state) {
return {
todos: state.get(‚todos‘).toJS() // Immer ein neues Objekt
}
}

Copy

Wenn der shallow check fehlschlägt, wird React-Redux die Komponente neu rendern. Wenn toJS() in mapStateToProps auf diese Weise verwendet wird, wird die Komponente immer neu gerendert, auch wenn sich der Wert nie ändert, was sich stark auf die Performance auswirkt.

Dies kann durch die Verwendung von toJS() in einer Higher Order Component verhindert werden, wie im Abschnitt Best Practices weiter unten beschrieben.

Weitere Informationen#

Artikel

  • Immutable.js, persistente Datenstrukturen und gemeinsame Nutzung von Strukturen
  • Immutable Data Structures and JavaScript
  • React.js pure render performance anti-pattern
  • Building Efficient UI with React and Redux

Chrome Extension

  • Immutable Object Formatter

Is Using Immutable.JS worth the effort?#

Frequently, yes. Es gibt verschiedene Kompromisse und Meinungen zu berücksichtigen, aber es gibt viele gute Gründe, Immutable.JS zu verwenden. Unterschätzen Sie nicht die Schwierigkeit, eine Eigenschaft Ihres Zustandsbaums ausfindig zu machen, die versehentlich geändert wurde.

Komponenten werden sowohl neu gerendert, wenn sie nicht gerendert werden sollten, als auch nicht gerendert, wenn sie gerendert werden sollten, und es ist schwierig, den Fehler ausfindig zu machen, der das Rendering-Problem verursacht, da die Komponente, die falsch gerendert wird, nicht unbedingt diejenige ist, deren Eigenschaften versehentlich geändert wurden.

Dieses Problem wird hauptsächlich durch die Rückgabe eines geänderten Zustandsobjekts von einem Redux-Reduzierer verursacht. Mit Immutable.JS gibt es dieses Problem einfach nicht, wodurch eine ganze Klasse von Fehlern aus Ihrer App entfernt wird.

Dies, zusammen mit der Leistung und der reichhaltigen API für die Datenmanipulation, ist der Grund, warum Immutable.JS die Mühe wert ist.

Weitere Informationen#

Dokumentation

  • Fehlerbehebung: Nothing happens when I dispatch an action

What are some opinionated Best Practices for using Immutable.JS with Redux?#

Immutable.JS can provide significant reliability and performance improvements to your app, but it must be used correctly. Wenn Sie sich für die Verwendung von Immutable.JS entscheiden (und denken Sie daran, Sie sind nicht dazu verpflichtet, und es gibt andere unveränderliche Bibliotheken, die Sie verwenden können), befolgen Sie die folgenden Best Practices, und Sie werden in der Lage sein, das Beste daraus zu machen, ohne über eines der Probleme zu stolpern, die es potenziell verursachen kann.

Mischen Sie niemals einfache JavaScript-Objekte mit Immutable.JS#

Lassen Sie niemals ein einfaches JavaScript-Objekt Immutable.JS-Eigenschaften enthalten. Ebenso darf ein Immutable.JS-Objekt niemals ein einfaches JavaScript-Objekt enthalten.

Weitere Informationen#

Artikel

  • Immutable Datenstrukturen und JavaScript

Machen Sie Ihren gesamten Redux-Statusbaum zu einem Immutable.JS-Objekt#

Für eine Redux-Anwendung sollte Ihr gesamter Zustandsbaum ein Immutable.JS-Objekt sein, wobei überhaupt keine einfachen JavaScript-Objekte verwendet werden sollten.

  • Erstellen Sie den Baum mithilfe der fromJS()-Funktion von Immutable.JS.

  • Verwenden Sie eine Immutable.JS-fähige Version der Funktion combineReducers, wie die in redux-immutable, da Redux selbst erwartet, dass der Zustandsbaum ein einfaches JavaScript-Objekt ist.

  • Wenn Sie JavaScript-Objekte zu einer Immutable.JS-Map oder -Liste mit Immutable.JS-Methoden update, merge oder set hinzufügen, stellen Sie sicher, dass das hinzugefügte Objekt zunächst mit fromJS() in ein Immutable-Objekt konvertiert wird.

Beispiel

// vermeiden
const newObj = { key: value }
const newState = state.setIn(, newObj)
// newObj wurde als einfaches JavaScript-Objekt hinzugefügt, NICHT als Immutable.JS Map
// empfohlen
const newObj = { key: value }
const newState = state.setIn(, fromJS(newObj))
// newObj ist jetzt eine Immutable.JS Map

Kopieren

Weitere Informationen#

Artikel

  • Immutable Data Structures and JavaScript

Bibliotheken

  • redux-immutable

Verwenden Sie Immutable.JS überall, außer in Ihren dummen Komponenten#

Wenn Sie Immutable.JS überall verwenden, bleibt Ihr Code performant. Verwenden Sie es in Ihren intelligenten Komponenten, Ihren Selektoren, Ihren Sagas oder Thunks, Action Creators und vor allem in Ihren Reduzierern.

Verwenden Sie Immutable.JS jedoch nicht in Ihren dummen Komponenten.

Weitere Informationen#

Artikel

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

Beschränken Sie die Verwendung von toJS()#

toJS() ist eine teure Funktion und negiert den Zweck der Verwendung von Immutable.JS. Vermeiden Sie ihre Verwendung.

Weitere Informationen#

Diskussionen

  • Lee Byron auf Twitter: „Perf tip for #immutablejs…“

Ihre Selektoren sollten Immutable.JS-Objekte zurückgeben#

Immer. Diese Praxis hat mehrere Vorteile:

  • Sie vermeidet unnötige Wiederholungen, die durch den Aufruf von .toJS() in Selektoren verursacht werden (da .toJS() immer ein neues Objekt zurückgibt).
    • Es ist möglich, Selektoren, in denen Sie .toJS() aufrufen, zu memoisieren, aber das ist überflüssig, wenn es ausreicht, Immutable.js-Objekte ohne Memoisierung zurückzugeben.
  • Es etabliert eine konsistente Schnittstelle für Selektoren; Sie müssen nicht verfolgen, ob ein Immutable.js-Objekt oder ein einfaches JavaScript-Objekt zurückgegeben wird.

Verwenden Sie Immutable.JS-Objekte in Ihren Smart Components#

Smart Components, die über die connect-Funktion von React Redux auf den Store zugreifen, müssen die Immutable.JS-Werte verwenden, die von Ihren Selektoren zurückgegeben werden. Stellen Sie sicher, dass Sie die potenziellen Probleme vermeiden, die dies durch unnötiges Neudarstellen von Komponenten verursachen kann. Memoize Ihre Selektoren mit einer Bibliothek wie reselect, wenn nötig.

Weitere Informationen#

Dokumentation

  • Rezepte: Berechnen abgeleiteter Daten
  • FAQ: Unveränderliche Daten
  • Reselect Dokumentation: Wie verwende ich Reselect mit Immutable.js?

Artikel

  • Redux Patterns und Anti-Patterns

Bibliotheken

  • Reselect: Selector library for Redux

Never use toJS() in mapStateToProps#

Die Konvertierung eines Immutable.JS-Objekts in ein JavaScript-Objekt mit toJS() wird jedes Mal ein neues Objekt zurückgeben. Wenn Sie dies in mapStateToProps tun, führen Sie dazu, dass die Komponente jedes Mal, wenn sich der Zustandsbaum ändert, glaubt, dass sich das Objekt geändert hat, und lösen so eine unnötige Neudarstellung aus.

Weitere Informationen#

Dokumentation

  • FAQ: Immutable Data

Verwenden Sie niemals Immutable.JS in Ihren Dumb Components#

Ihre Dumb Components sollten rein sein, d.h. sie sollten bei gleicher Eingabe die gleiche Ausgabe erzeugen und keine externen Abhängigkeiten haben. Wenn Sie einer solchen Komponente ein Immutable.JS-Objekt als Requisite übergeben, machen Sie sie von Immutable.JS abhängig, um den Wert der Requisite zu extrahieren und anderweitig zu manipulieren.

Eine solche Abhängigkeit macht die Komponente unrein, erschwert das Testen der Komponente und macht die Wiederverwendung und das Refactoring der Komponente unnötig schwierig.

Weitere Informationen#

Artikel

  • Immutable Data Structures and JavaScript
  • Smart and Dumb Components in React
  • Tips For a Better Redux Architecture: Lessons for Enterprise Scale

Verwenden Sie eine Higher Order Component, um die Immutable.JS-Props Ihrer Smart Component in die JavaScript-Props Ihrer Dumb Component zu konvertieren#

Etwas muss die Immutable.JS-Props in Ihrer Smart Component auf die reinen JavaScript-Props in Ihrer Dumb Component abbilden. Dieses Etwas ist eine Higher Order Component (HOC), die einfach die Immutable.JS-Props aus Ihrer Smart Component nimmt und sie mit toJS() in reine JavaScript-Props umwandelt, die dann an Ihre Dumb Component übergeben werden.

Ein Beispiel für eine solche HOC folgt. Eine ähnliche HOC ist als NPM-Paket verfügbar: 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

Und so würden Sie es in Ihrer Smart Component verwenden:

import { connect } from ‚react-redux‘

import { toJS } from ‚./to-js‘

import DumbComponent from ‚./dumb.component‘
const mapStateToProps = state => {
return {
// obj ist ein Immutable-Objekt in Smart Component, wird aber von toJS in ein einfaches
// JavaScript-Objekt umgewandelt und so als reines JavaScript
// Objekt an DumbComponent übergeben. Da es sich hier in mapStateToProps aber immer noch um ein Immutable.JS-Objekt handelt,
// gibt es kein Problem mit fehlerhaften Re-Renderings.
obj: getImmutableObjectFromStateTree(state)
}
}
export default connect(mapStateToProps)(toJS(DumbComponent))

Copy

Durch die Konvertierung von Immutable.JS-Objekten in einfache JavaScript-Werte innerhalb einer HOC erreichen wir die Portabilität der Dumb Component, aber ohne die Leistungseinbußen, die die Verwendung von toJS() in der Smart Component mit sich bringt.

Hinweis: Wenn Ihre Anwendung eine hohe Leistung erfordert, müssen Sie toJS() möglicherweise ganz vermeiden und müssen daher Immutable.JS in Ihren Dumb Components verwenden. Für die meisten Anwendungen wird dies jedoch nicht der Fall sein, und die Vorteile, die sich ergeben, wenn Sie Immutable.JS nicht in Ihren Dumb Components verwenden (Wartbarkeit, Portabilität und leichteres Testen), überwiegen bei weitem alle wahrgenommenen Leistungsverbesserungen, die sich durch die Verwendung von Immutable.JS ergeben.

Darüber hinaus sollte die Verwendung von toJS in einer Higher Order Component, wenn überhaupt, nur geringe Leistungseinbußen verursachen, da die Komponente nur aufgerufen wird, wenn sich die Requisiten der verbundenen Komponente ändern. Wie bei jedem Leistungsproblem sollten Sie zunächst Leistungstests durchführen, bevor Sie entscheiden, was Sie optimieren wollen.

Weitere Informationen#

Dokumentation

  • React: Higher-Order Components

Artikel

  • React Higher Order Components in depth

Discussions

  • Reddit: acemarke und cpsubrian kommentieren Dan Abramov: Redux ist keine Architektur oder Design Pattern, es ist nur eine Bibliothek.

Gists

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

Use the Immutable Object Formatter Chrome Extension to Aid Debugging#

Installieren Sie den Immutable Object Formatter , und inspizieren Sie Ihre Immutable.JS Daten ohne das Rauschen der Immutable.JS eigenen Objekteigenschaften zu sehen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.