Utiliser Immutable.JS avec Redux#

Table des matières#

  • Pourquoi devrais-je utiliser une bibliothèque axée sur l’immuable comme Immutable.JS?
  • Pourquoi devrais-je choisir Immutable.JS comme bibliothèque immuable?
  • Quels sont les problèmes liés à l’utilisation d’Immutable.JS?
  • Est-ce qu’Immutable.JS en vaut la peine ?
  • Quelles sont les meilleures pratiques selon l’opinion pour utiliser Immutable.JS avec Redux?

Pourquoi devrais-je utiliser une bibliothèque axée sur l’immuable comme Immutable.JS?#

Les bibliothèques axées sur l’immuable comme Immutable.JS ont été conçues pour surmonter les problèmes d’immuabilité inhérents à JavaScript, en offrant tous les avantages de l’immuabilité avec les performances dont votre application a besoin.

Que vous choisissiez d’utiliser une telle bibliothèque, ou de vous en tenir au JavaScript ordinaire, dépend de votre degré d’aisance à ajouter une autre dépendance à votre application, ou de votre certitude de pouvoir éviter les pièges inhérents à l’approche de l’immuabilité par JavaScript.

Quelle que soit l’option choisie, assurez-vous de bien connaître les concepts d’immuabilité, d’effets secondaires et de mutation. En particulier, assurez-vous d’avoir une compréhension approfondie de ce que JavaScript fait lors de la mise à jour et de la copie de valeurs afin de vous prémunir contre les mutations accidentelles qui dégraderont les performances de votre application, voire la briseront complètement.

Autres informations#

Documentation

  • Recettes : immutabilité, effets secondaires et mutation

Articles

  • Introduction à Immutable.js et les concepts de programmation fonctionnelle
  • Pros et Cons de l’utilisation de l’immuabilité avec React.js

Pourquoi devrais-je choisir Immutable.JS comme bibliothèque immuable?#

Immutable.JS a été conçu pour fournir l’immuabilité d’une manière performante dans un effort pour surmonter les limites de l’immuabilité avec JavaScript. Ses principaux avantages comprennent :

Immutabilité garantie#

Les données encapsulées dans un objet Immutable.JS ne sont jamais mutées. Une nouvelle copie est toujours renvoyée. Cela contraste avec JavaScript, dans lequel certaines opérations ne mutent pas vos données (par exemple, certaines méthodes Array, y compris map, filter, concat, forEach, etc.), mais certaines le font (pop, push, splice, etc.).

Riche API#

Immutable.JS fournit un riche ensemble d’objets immuables pour encapsuler vos données (par ex.par exemple, les cartes, les listes, les ensembles, les enregistrements, etc.), et un ensemble étendu de méthodes pour les manipuler, y compris des méthodes pour trier, filtrer et grouper les données, les inverser, les aplatir et créer des sous-ensembles.

Performance#

Immutable.JS fait beaucoup de travail en coulisse pour optimiser les performances. C’est la clé de sa puissance, car l’utilisation de structures de données immuables peut impliquer beaucoup de copies coûteuses. En particulier, la manipulation immuable de grands ensembles de données complexes, tels qu’un arbre d’état Redux imbriqué, peut générer de nombreuses copies intermédiaires d’objets, qui consomment de la mémoire et ralentissent les performances alors que le ramasseur d’ordures du navigateur se bat pour nettoyer les choses.

Immutable.JS évite cela en partageant intelligemment les structures de données sous la surface, minimisant ainsi le besoin de copier les données. Il permet également d’effectuer des chaînes d’opérations complexes sans créer de données intermédiaires clonées inutiles (et coûteuses) qui seront rapidement jetées.

Vous ne voyez jamais cela, bien sûr – les données que vous donnez à un objet Immutable.JS ne sont jamais mutées. Ce sont plutôt les données intermédiaires générées dans Immutable.JS à partir d’une séquence enchaînée d’appels de méthodes qui sont libres d’être mutées. Vous obtenez donc tous les avantages des structures de données immuables sans aucun (ou très peu) des coups potentiels de performance.

Informations complémentaires#

Articles

  • Immutable.js, structures de données persistantes et partage structurel
  • PDF : Immutabilité JavaScript – Ne pas aller changer

Librairies

  • Immutable.js

Quels sont les problèmes liés à l’utilisation d’Immutable.JS?#

Bien que puissant, Immutable.JS doit être utilisé avec précaution, car il vient avec des problèmes qui lui sont propres. Notez, cependant, que tous ces problèmes peuvent être surmontés assez facilement avec un codage prudent.

Difficile d’interopérer avec#

JavaScript ne fournit pas de structures de données immuables. Ainsi, pour qu’Immutable.JS puisse fournir ses garanties d’immuabilité, vos données doivent être encapsulées dans un objet Immutable.JS (comme un Map ou un List, etc.). Une fois qu’elles sont contenues de cette manière, il est difficile pour ces données d’interagir ensuite avec d’autres objets JavaScript ordinaires.

Par exemple, vous ne pourrez plus référencer les propriétés d’un objet par le biais de la notation standard JavaScript par points ou crochets. Au lieu de cela, vous devrez les référencer via les méthodes get() ou getIn() d’Immutable.JS, qui utilisent une syntaxe maladroite qui accède aux propriétés via un tableau de chaînes, chacune d’entre elles représentant une clé de propriété.

Par exemple, au lieu de myObj.prop1.prop2.prop3, vous utiliserez myImmutableMap.getIn().

Ceci rend maladroit l’interopérabilité non seulement avec votre propre code, mais aussi avec d’autres bibliothèques, telles que lodash ou ramda, qui s’attendent à des objets JavaScript simples.

Notez que les objets Immutable.JS ont une méthode toJS(), qui renvoie les données comme une structure de données JavaScript ordinaire, mais cette méthode est extrêmement lente, et l’utiliser intensivement annulera les avantages de performance que Immutable.JS fournit

Une fois utilisé, Immutable.JS se répandra dans votre codebase#

Une fois que vous encapsulez vos données avec Immutable.JS, vous devez utiliser les accesseurs de propriété get() ou getIn() d’Immutable.JS pour y accéder.

Cela a pour effet de répandre Immutable.JS dans l’ensemble de votre codebase, y compris potentiellement vos composants, où vous pouvez préférer ne pas avoir de telles dépendances externes. Votre codebase entière doit savoir ce qui est, et ce qui n’est pas, un objet Immutable.JS. Cela rend également la suppression d’Immutable.JS de votre application difficile à l’avenir, si jamais vous en avez besoin.

Ce problème peut être évité en découplant votre logique d’application de vos structures de données, comme indiqué dans la section des meilleures pratiques ci-dessous.

Pas de déstructuration ou d’opérateurs d’étalement#

Parce que vous devez accéder à vos données via Immutable.JS’s own get() and getIn() methods, vous ne pouvez plus utiliser l’opérateur de déstructuration de JavaScript (ou l’opérateur d’étalement d’objet proposé), ce qui rend votre code plus verbeux.

Non adapté aux petites valeurs qui changent souvent#

Immutable.JS fonctionne mieux pour les collections de données, et plus elles sont grandes, mieux c’est. Il peut être lent lorsque vos données comprennent beaucoup de petits objets JavaScript simples, chacun comprenant quelques clés de valeurs primitives.

Notez cependant que cela ne s’applique pas à l’arbre d’état de Redux, qui est (généralement) représenté comme une grande collection de données.

Difficile à déboguer#

Les objets Immutable.JS, tels que Map, List, etc, peuvent être difficiles à déboguer, car l’inspection d’un tel objet révélera toute une hiérarchie imbriquée de propriétés spécifiques à Immutable.JS dont vous ne vous souciez pas, tandis que vos données réelles dont vous vous souciez sont encapsulées à plusieurs couches de profondeur.

Pour résoudre ce problème, utilisez une extension de navigateur telle que Immutable.js Object Formatter, qui fait apparaître vos données dans Chrome Dev Tools, et masque les propriétés d’Immutable.JS lors de l’inspection de vos données.

Casse les références d’objets, entraînant de mauvaises performances#

L’un des principaux avantages de l’immutabilité est qu’elle permet une vérification d’égalité superficielle, ce qui améliore considérablement les performances.

Si deux variables différentes font référence au même objet immuable, alors une simple vérification d’égalité des deux variables suffit pour déterminer qu’elles sont égales et que l’objet auquel elles font toutes deux référence est inchangé. La vérification de l’égalité n’a jamais à vérifier les valeurs d’aucune des propriétés de l’objet, car il est, bien sûr, immuable.

Cependant, la vérification superficielle ne fonctionnera pas si vos données encapsulées dans un objet Immutable.JS sont elles-mêmes un objet. C’est parce que la méthode toJS() d’Immutable.JS, qui renvoie les données contenues dans un objet Immutable.JS comme une valeur JavaScript, créera un nouvel objet chaque fois qu’elle sera appelée, et brisera ainsi la référence avec les données encapsulées.

En conséquence, appeler toJS() deux fois, par exemple, et affecter le résultat à deux variables différentes fera échouer une vérification d’égalité sur ces deux variables, même si les valeurs des objets eux-mêmes n’ont pas changé.

C’est un problème particulier si vous utilisez toJS() dans la fonction mapStateToProps d’un composant enveloppé, car React-Redux compare superficiellement chaque valeur dans l’objet props retourné. Par exemple, la valeur référencée par le todos prop retourné par mapStateToProps ci-dessous sera toujours un objet différent, et échouera donc à une vérification d’égalité peu profonde.

// ÉVITER .toJS() dans mapStateToProps
function mapStateToProps(state) {
return {
todos : state.get(‘todos’).toJS() // Toujours un nouvel objet
}
}

Copy

Lorsque la vérification superficielle échoue, React-Redux provoque un nouveau rendu du composant. Utiliser toJS() dans mapStateToProps de cette façon, donc, provoquera toujours un nouveau rendu du composant, même si la valeur ne change jamais, ce qui a un impact important sur les performances.

Cela peut être évité en utilisant toJS() dans un composant d’ordre supérieur, comme discuté dans la section Meilleures pratiques ci-dessous.

Informations supplémentaires#

Articles

  • Immutable.js, structures de données persistantes et partage structurel
  • Structures de données immuables et JavaScript
  • React.js pure render performance anti-pattern
  • Building Efficient UI with React and Redux

Chrome Extension

  • Immutable Object Formatter

L’utilisation d’Immutable.JS vaut-elle la peine ? #

Fréquemment, oui. Il y a divers compromis et opinions à considérer, mais il y a beaucoup de bonnes raisons d’utiliser Immutable.JS. Ne sous-estimez pas la difficulté d’essayer de retrouver une propriété de votre arbre d’état qui a été mutée par inadvertance.

Les composants vont à la fois se re-rendre quand ils ne devraient pas, et refuser de se rendre quand ils le devraient, et retrouver le bogue qui cause le problème de rendu est difficile, car le composant qui se rend de manière incorrecte n’est pas nécessairement celui dont les propriétés sont accidentellement mutées.

Ce problème est causé principalement par le retour d’un objet d’état muté à partir d’un réducteur Redux. Avec Immutable.JS, ce problème n’existe tout simplement pas, éliminant ainsi toute une classe de bogues de votre application.

Ceci, ainsi que ses performances et son API riche pour la manipulation des données, est la raison pour laquelle Immutable.JS vaut la peine de faire des efforts.

Informations supplémentaires#

Documentation

  • Dépannage : Rien ne se passe lorsque je dispatche une action

Quelles sont les meilleures pratiques d’opinion pour utiliser Immutable.JS avec Redux ? #

Immutable.JS peut fournir des améliorations significatives de fiabilité et de performance à votre application, mais il doit être utilisé correctement. Si vous choisissez d’utiliser Immutable.JS (et rappelez-vous, vous n’êtes pas obligé de le faire, et il existe d’autres bibliothèques immuables que vous pouvez utiliser), suivez ces meilleures pratiques avisées, et vous serez en mesure d’en tirer le meilleur parti, sans trébucher sur l’un des problèmes qu’il peut potentiellement causer.

Ne jamais mélanger des objets JavaScript ordinaires avec Immutable.JS#

Ne jamais laisser un objet JavaScript ordinaire contenir des propriétés Immutable.JS. De même, ne laissez jamais un objet Immutable.JS contenir un objet JavaScript ordinaire.

Plus d’informations#

Articles

  • Structures de données immuables et JavaScript

Faites de votre arbre d’état Redux entier un objet Immutable.JS#

Pour une application Redux, votre arbre d’état entier devrait être un objet Immutable.JS, sans aucun objet JavaScript ordinaire utilisé.

  • Créer l’arbre en utilisant la fonction fromJS() d’Immutable.JS.

  • Utiliser une version d’Immutable.JS de la fonction combineReducers, comme celle de redux-immutable, car Redux lui-même s’attend à ce que l’arbre d’état soit un objet JavaScript ordinaire.

  • Lorsque vous ajoutez des objets JavaScript à une carte ou une liste Immutable.JS en utilisant Immutable.JS update, merge ou set, assurez-vous que l’objet ajouté est d’abord converti en objet Immutable en utilisant fromJS().

Exemple

// éviter
const newObj = {key : value }
const newState = state.setIn(, newObj)
// newObj a été ajouté comme un objet JavaScript ordinaire, PAS comme une carte Immutable.JS
// recommandé
const newObj = {key : value }
const newState = state.setIn(, fromJS(newObj))
// newObj est maintenant une carte Immutable.JS

Copy

Informations complémentaires#

Articles

  • Structures de données immuables et JavaScript

Librairies

  • redux-immutable

Utilisez Immutable.JS partout sauf dans vos composants muets#

Utiliser Immutable.JS partout permet de garder votre code performant. Utilisez-le dans vos composants intelligents, vos sélecteurs, vos sagas ou thunks, vos créateurs d’actions, et surtout vos réducteurs.

N’utilisez pas, cependant, Immutable.JS dans vos composants muets.

Informations complémentaires#

Articles

  • Structures de données immuables et JavaScript
  • Composants intelligents et muets dans React

Limitez votre utilisation de toJS()#

toJS() est une fonction coûteuse et annule le but de l’utilisation d’Immutable.JS. Évitez son utilisation.

Informations complémentaires#

Discussions

  • Lee Byron sur Twitter : « Perf astuce pour #immutablejs… »

Vos sélecteurs doivent retourner des objets Immutable.JS#

Toujours. Cette pratique présente plusieurs avantages :

  • Elle évite les rerendus inutiles causés par l’appel de .toJS() dans les sélecteurs (puisque .toJS() renverra toujours un nouvel objet).
    • Il est possible de mémoriser les sélecteurs où vous appelez .toJS(), mais c’est redondant lorsque le simple fait de retourner des objets Immutable.js sans mémorisation suffira.
  • Il établit une interface cohérente pour les sélecteurs ; vous n’aurez pas à garder la trace de savoir si un objet Immutable.js ou un objet JavaScript ordinaire sera retourné.

Utiliser les objets Immutable.JS dans vos composants intelligents#

Les composants intelligents qui accèdent au magasin via la fonction connect de React Redux doivent utiliser les valeurs Immutable.JS retournées par vos sélecteurs. Assurez-vous d’éviter les problèmes potentiels que cela peut causer avec le re-rendu inutile des composants. Mémorisez vos sélecteurs en utilisant une bibliothèque telle que reselect si nécessaire.

Plus d’informations#

Documentation

  • Recettes : Calcul des données dérivées
  • FAQ : Données immuables
  • Documentation Reselect : Comment utiliser Reselect avec Immutable.js?

Articles

  • Redux Patterns et Anti-Patterns

Librairies

  • Reselect : Bibliothèque de sélecteurs pour Redux

Ne jamais utiliser toJS() dans mapStateToProps#

Convertir un objet Immutable.JS en un objet JavaScript en utilisant toJS() renverra un nouvel objet à chaque fois. Si vous faites cela dans mapStateToProps, vous ferez croire au composant que l’objet a changé chaque fois que l’arbre d’état change, et déclencherez ainsi un re-rendu inutile.

Informations supplémentaires#

Documentation

  • FAQ : Données immuables

Ne jamais utiliser Immutable.JS dans vos composants muets#

Vos composants muets doivent être purs, c’est-à-dire qu’ils doivent produire la même sortie étant donné la même entrée, et ne pas avoir de dépendances externes. Si vous passez à un tel composant un objet Immutable.JS comme prop, vous le rendez dépendant d’Immutable.JS pour extraire la valeur du prop et le manipuler autrement.

Une telle dépendance rend le composant impur, rend les tests du composant plus difficiles, et rend la réutilisation et la refactorisation du composant inutilement difficile.

Plus d’informations#

Articles

  • Structures de données immuables et JavaScript
  • Composants intelligents et idiots dans React
  • Tips pour une meilleure architecture Redux : Lessons for Enterprise Scale

Utiliser un composant d’ordre supérieur pour convertir les props Immutable.JS de votre composant intelligent en props JavaScript de votre composant muet#

Quelque chose doit faire correspondre les props Immutable.JS de votre composant intelligent aux props JavaScript purs utilisés dans votre composant muet. Ce quelque chose est un composant d’ordre supérieur (HOC) qui prend simplement les props Immutable.JS de votre composant intelligent, et les convertit en utilisant toJS() en props JavaScript pur, qui sont ensuite passés à votre composant Dumb.

Un exemple d’un tel HOC suit. Un COH similaire est disponible en tant que paquet NPM pour votre commodité : 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

Et voici comment vous l’utiliseriez dans votre composant intelligent :

import { connect } from ‘react-redux’
import { toJS } from ‘./to-js’
import DumbComponent from ‘./dumb.component’
const mapStateToProps = state => {
return {
// obj est un objet Immutable dans Smart Component, mais il est converti en un simple
// objet JavaScript par toJS, et donc passé à DumbComponent comme un pur
// objet JavaScript. Parce que c’est toujours un objet Immutable.JS ici dans mapStateToProps, cependant,
// il n’y a pas de problème avec les re-rendus errants.
obj : getImmutableObjectFromStateTree(state)
}
}
export default connect(mapStateToProps)(toJS(DumbComponent))

Copy

En convertissant les objets Immutable.JS en valeurs JavaScript simples au sein d’un HOC, nous obtenons la portabilité du DumbComponent, mais sans les pertes de performance liées à l’utilisation de toJS() dans le Smart Component.

Note : si votre application nécessite de hautes performances, vous devrez peut-être éviter complètement toJS(), et donc utiliser Immutable.JS dans vos dumb components. Cependant, pour la plupart des apps, ce ne sera pas le cas, et les avantages de garder Immutable.JS hors de vos composants muets (maintenabilité, portabilité et tests plus faciles) dépasseront de loin toute amélioration de performance perçue de le garder dans.

En outre, l’utilisation de toJS dans un composant d’ordre supérieur ne devrait pas causer beaucoup, voire aucune, dégradation de performance, car le composant ne sera appelé que lorsque les props du composant connecté changent. Comme pour tout problème de performance, effectuez d’abord des vérifications de performance avant de décider ce qu’il faut optimiser.

Autres informations#

Documentation

  • React : Composants d’ordre supérieur

Articles

  • React Composants d’ordre supérieur en profondeur

Discussions

  • Reddit : acemarke et cpsubrian commentent Dan Abramov : Redux n’est pas une architecture ou un modèle de conception, c’est juste une bibliothèque.

Gistes

  • cpsubrian : Des décorateurs React pour les composants ‘intelligents’ redux/react-router/immutable

Utiliser l’extension Chrome Immutable Object Formatter pour aider au débogage#

Installer l’Immutable Object Formatter , et inspecter vos données Immutable.JS sans voir le bruit des propriétés d’objets propres à Immutable.JS.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.