portaldacalheta.pt
  • Principal
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
Back-End

Immuabilité en JavaScript avec Redux



Dans un écosystème toujours plus grand d’applications JavaScript riches et complexes, il y a plus d’états à gérer que jamais: l’utilisateur actuel, la liste des publications chargées, etc.

Tout ensemble de données nécessitant un historique des événements peut être considéré comme avec état. La gestion de l'état peut être difficile et sujette aux erreurs, mais travailler avec des données immuables (plutôt que mutables) et certaines technologies de support, à savoir Redux, aux fins de cet article, peut aider de manière significative.



Les données immuables ont des restrictions, à savoir qu'elles ne peuvent pas être modifiées une fois qu'elles ont été créées, mais elles présentent également de nombreux avantages, en particulier en ce qui concerne l'égalité entre les références et les valeurs, ce qui peut considérablement accélérer les applications qui reposent sur des comparaisons fréquentes de données (vérifier si quelque chose doit être mis à jour , par exemple).



L'utilisation d'états immuables nous permet d'écrire du code qui peut rapidement dire si l'état a changé, sans avoir besoin de faire une comparaison récursive sur les données, ce qui est généralement beaucoup, beaucoup plus rapide.



Cet article couvrira les applications pratiques de Redux lors de la gestion de l'état via des créateurs d'action, des fonctions pures, des réducteurs composés, des actions impures avec Redux-saga et Redux Thunk et, enfin, l'utilisation de Redux avec React. Cela dit, il existe de nombreuses alternatives à Redux, telles que les bibliothèques basées sur MobX, Relay et Flux.

Pourquoi Redux?

L'aspect clé qui sépare Redux de la plupart des autres conteneurs d'état tels que MobX, Relay et la plupart des autres implémentations basées sur Flux est que Redux a un seul état qui ne peut être modifié que via des «actions» (objets JavaScript simples), qui sont distribuées au Magasin Redux. La plupart des autres magasins de données ont l'état contenu dans les composants React eux-mêmes, vous permettent d'avoir plusieurs magasins et / ou d'utiliser l'état mutable.



Cela entraîne à son tour le réducteur du magasin, une fonction pure qui opère sur des données immuables, à exécuter et potentiellement mettre à jour l'état. Ce processus applique un flux de données unidirectionnel, qui est plus facile à comprendre et plus déterministe.

Le Redux Flow.



Puisque les réducteurs Redux sont des fonctions pures fonctionnant sur des données immuables, ils produisent toujours la même sortie avec la même entrée, ce qui les rend faciles à tester. Voici un exemple de réducteur:

import Immutable from 'seamless-immutable' const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { return state.concat(action.payload) } return state // note that a reducer MUST return a value } // somewhere else... store.dispatch({ type: 'USERS_ADD', payload: user }) // dispatch an action that causes the reducer to execute and add the user

La gestion des fonctions pures permet à Redux de prendre facilement en charge de nombreux cas d'utilisation qui ne sont généralement pas faciles à réaliser avec un état mutatif, tels que:



  • Voyage dans le temps (retour dans le temps à un état antérieur)
  • Journalisation (suivez chaque action pour déterminer ce qui a causé une mutation dans le magasin)
  • Environnements collaboratifs (tels que GoogleDocs, où les actions sont des objets JavaScript simples et peuvent être sérialisées, envoyées via le réseau et relues sur une autre machine)
  • Rapport de bogue facile (envoyez simplement la liste des actions distribuées et rejouez-les pour obtenir exactement le même état)
  • Rendu optimisé (au moins dans les frameworks qui rendent le DOM virtuel en fonction de l'état, comme React: en raison de l'immuabilité, vous pouvez facilement dire si quelque chose a changé en comparant les références, par opposition à la comparaison récursive des objets)
  • Testez facilement vos réducteurs, car les fonctions pures peuvent facilement être testées à l'unité

Créateurs d'action

Les créateurs d'action de Redux aident à garder le code propre et testable. N'oubliez pas que les «actions» dans Redux ne sont rien de plus que des objets JavaScript simples décrivant une mutation qui devrait se produire. Cela étant dit, écrire les mêmes objets encore et encore est répétitif et sujet aux erreurs.

apprentissage semi-supervisé apprentissage profond

Un créateur d'action dans Redux est simplement une fonction d'assistance qui renvoie un objet JavaScript simple décrivant une mutation. Cela permet de réduire le code répétitif et de conserver toutes vos actions au même endroit:



export function usersFetched(users) { return { type: 'USERS_FETCHED', payload: users, } } export function usersFetchFailed(err) { return { type: 'USERS_FETCH_FAILED', payload: err, } } // reducer somewhere else... const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function usersFetchedReducer(state = initialState, action) { if (action.type === 'USERS_FETCHED') { return Immutable(action.payload) } return state // note that a reducer MUST return a value }

Utilisation de Redux avec des bibliothèques immuables

Bien que la nature même des réducteurs et des actions les rend faciles à tester, sans bibliothèque d'aide à l'immuabilité, rien ne vous protège des objets en mutation, ce qui signifie que les tests de tous vos réducteurs doivent être particulièrement robustes.

Prenons l'exemple de code suivant d'un problème que vous rencontrerez sans bibliothèque pour vous protéger:



const initialState = [] function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { state.push(action.payload) // NOTE: mutating action!! return state } return state // note that a reducer MUST return a value }

Dans cet exemple de code, le voyage dans le temps sera interrompu car l'état précédent sera désormais le même que l'état actuel, les composants purs peuvent potentiellement ne pas être mis à jour (ou restituer) car la référence à l'état n'a pas changé même si les données contient a changé et les mutations sont beaucoup plus difficiles à raisonner.

Sans bibliothèque d'immuabilité, nous perdons tous les avantages fournis par Redux. Il est donc fortement recommandé d’utiliser une bibliothèque d’aide à l’immuabilité, telle que immutable.js ou parfaitement immuable, en particulier lorsque vous travaillez dans une grande équipe avec plusieurs mains touchant le code.

Quelle que soit la bibliothèque que vous utilisez, Redux se comportera de la même manière. Comparons les avantages et les inconvénients des deux afin de pouvoir choisir celui qui convient le mieux à votre cas d'utilisation:

Immutable.js

Immutable.js est une bibliothèque, construite par Facebook, avec un style plus fonctionnel sur les structures de données, telles que les cartes, les listes, les ensembles et les séquences. Sa bibliothèque de structures de données persistantes immuables effectue le moins de copies possible entre les différents états.

Avantages:

la loi sur le steagall en verre abroger les conséquences
  • Partage structurel
  • Plus efficace lors des mises à jour
  • Plus efficace en mémoire
  • Dispose d'une suite de méthodes d'aide pour gérer les mises à jour

Les inconvénients:

  • Ne fonctionne pas de manière transparente avec les bibliothèques JS existantes (c'est-à-dire lodash, ramda)
  • Nécessite une conversion vers et depuis (toJS / fromJS), en particulier pendant l'hydratation / déshydratation et le rendu

Immuable sans couture

Seamless-immutable est une bibliothèque de données immuables qui est rétrocompatible jusqu'à ES5.

Il est basé sur des fonctions de définition de propriété ES5, telles que defineProperty(..) pour désactiver les mutations sur les objets. En tant que tel, il est entièrement compatible avec les bibliothèques existantes telles que lodash et Ramda. Il peut également être désactivé dans les versions de production, offrant un gain de performances potentiellement significatif.

Avantages:

  • Fonctionne de manière transparente avec les bibliothèques JS existantes (c'est-à-dire lodash, ramda)
  • Aucun code supplémentaire nécessaire pour prendre en charge la conversion
  • Les vérifications peuvent être désactivées dans les versions de production, ce qui augmente les performances

Les inconvénients:

  • Pas de partage structurel - les objets / tableaux sont copiés superficiellement, ce qui le rend plus lent pour les grands ensembles de données
  • Pas aussi efficace en mémoire

Redux et réducteurs multiples

Une autre fonctionnalité utile de Redux est la possibilité de composer des réducteurs ensemble, ce qui permet de créer des applications beaucoup plus compliquées, et dans une application de toute taille appréciable, vous aurez forcément plusieurs types d'états (utilisateur actuel, liste des articles chargés, etc). Redux prend en charge (et encourage) ce cas d'utilisation en fournissant naturellement la fonction combineReducers:

import { combineReducers } from 'redux' import currentUserReducer from './currentUserReducer' import postsListReducer from './postsListReducer' export default combineReducers({ currentUser: currentUserReducer, postsList: postsListReducer, })

Avec le code ci-dessus, vous pouvez avoir un composant qui repose sur le currentUser et un autre composant qui repose sur postsList. Cela améliore également les performances car tout composant unique ne s'abonnera qu'à la ou aux branches de l'arborescence les concernant.

Actions impures dans Redux

Par défaut, vous ne pouvez envoyer que des objets JavaScript simples à Redux. Cependant, avec le middleware, Redux peut prendre en charge des actions impures telles que l'obtention de l'heure actuelle, l'exécution d'une requête réseau, l'écriture d'un fichier sur le disque, etc.

«Middleware» est le terme utilisé pour les fonctions qui peuvent intercepter les actions en cours de distribution. Une fois intercepté, il peut faire des choses comme transformer l'action ou envoyer une action asynchrone, un peu comme le middleware dans d'autres frameworks (comme Express.js).

Deux bibliothèques middleware très courantes sont Redux Thunk et Redux-saga. Redux Thunk est écrit dans un style impératif, tandis que Redux-saga est écrit dans un style fonctionnel. Comparons les deux.

Redux Thunk

Redux Thunk prend en charge les actions impures dans Redux en utilisant des thunks, des fonctions qui renvoient d'autres fonctions chaînables. Pour utiliser Redux-Thunk, vous devez d'abord monter le middleware Redux Thunk sur le magasin:

import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( myRootReducer, applyMiddleware(thunk), // here, we apply the thunk middleware to R )

Maintenant, nous pouvons effectuer des actions impures (telles que l'exécution d'un appel API) en envoyant un thunk au magasin Redux:

store.dispatch( dispatch => { return api.fetchUsers() .then(users => dispatch(usersFetched(users)) // usersFetched is a function that returns a plain JavaScript object (Action) .catch(err => dispatch(usersFetchError(err)) // same with usersFetchError } )

Il est important de noter que l’utilisation de thunks peut rendre votre code difficile à tester et compliquer le raisonnement dans le flux de code.

Redux-saga

Redux-saga soutient les actions impures à travers un ES6 (ES2015) fonctionnalité appelée générateurs et une bibliothèque de helpers fonctionnels / purs. L'avantage des générateurs est qu'ils peuvent être repris et suspendus, et leur contrat API les rend extrêmement faciles à tester.

Voyons comment nous pouvons améliorer la lisibilité et la testabilité de la méthode Thunk précédente en utilisant des sagas!

Tout d'abord, montons le middleware Redux-saga dans notre boutique:

import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import rootReducer from './rootReducer' import rootSaga from './rootSaga' // create the saga middleware const sagaMiddleware = createSagaMiddleware() // mount the middleware to the store const store = createStore( rootReducer, applyMiddleware(sagaMiddleware), ) // run our saga! sagaMiddleware.run(rootSaga)

Notez que le run(..) La fonction doit être appelée avec la saga pour qu'elle commence à s'exécuter.

Créons maintenant notre saga:

import { call, put, takeEvery } from 'redux-saga/effects' // these are saga effects we'll use export function *fetchUsers(action) { try { const users = yield call(api.fetchUsers) yield put(usersFetched(users)) } catch (err) { yield put(usersFetchFailed(err)) } } export default function *rootSaga() { yield takeEvery('USERS_FETCH', fetchUsers) }

Nous avons défini deux fonctions génératrices, l'une qui récupère la liste des utilisateurs et le rootSaga. Notez que nous n’avons pas appelé api.fetchUsers directement, mais à la place, il l'a donné dans un objet d'appel. C'est parce que Redux-saga intercepte l'objet d'appel et exécute la fonction contenue à l'intérieur pour créer un environnement pur (en ce qui concerne vos générateurs).

rootSaga renvoie un seul appel à une fonction appelée takeEvery, qui prend chaque action distribuée avec un type de USERS_FETCH et appelle le fetchUsers saga avec l'action qu'elle a prise. Comme nous pouvons le voir, cela crée un modèle d'effets secondaires très prévisible pour Redux, ce qui le rend facile à tester!

Tester les Sagas

Voyons comment les générateurs rendent nos sagas faciles à tester. Nous utiliserons moka dans cette partie pour exécuter nos tests unitaires et chai pour les assertions.

pourquoi devrais-je utiliser node.js

Parce que les sagas produisent des objets JavaScript simples et sont exécutées dans un générateur, nous pouvons facilement tester qu'elles exécutent le bon comportement sans aucune moquerie! Gardez à l'esprit que call , take , put, etc. ne sont que des objets JavaScript simples qui sont interceptés par le middleware Redux-saga.

import { take, call } from 'redux-saga/effects' import { expect } from 'chai' import { rootSaga, fetchUsers } from '../rootSaga' describe('saga unit test', () => { it('should take every USERS_FETCH action', () => { const gen = rootSaga() // create our generator iterable expect(gen.next().value).to.be.eql(take('USERS_FETCH')) // assert the yield block does have the expected value expect(gen.next().done).to.be.equal(false) // assert that the generator loops infinitely }) it('should fetch the users if successful', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const users = [ user1, user2 ] // some mock response expect(gen.next(users).value).to.be.eql(put(usersFetched(users)) }) it('should fail if API fails', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const err = { message: 'authentication failed' } // some mock error expect(gen.throw(err).value).to.be.eql(put(usersFetchFailed(err)) }) })

Travailler avec React

Bien que Redux ne soit lié à aucune bibliothèque compagnon spécifique, il fonctionne particulièrement bien avec React.js puisque les composants React sont des fonctions pures qui prennent un état en entrée et produisent un DOM virtuel en sortie.

React-Redux est une bibliothèque d'aide pour React et Redux qui élimine la plupart du travail acharné reliant les deux. Pour utiliser React-Redux le plus efficacement possible, passons en revue la notion de composants de présentation et de composants de conteneur.

taux de facturation vs calculateur de salaire

Les composants de présentation décrivent à quoi les choses devraient ressembler visuellement, en fonction uniquement de leurs accessoires à rendre; ils invoquent des rappels depuis les accessoires pour envoyer des actions. Ils sont écrits à la main, totalement purs et ne sont pas liés à des systèmes de gestion d’états comme Redux.

Les composants du conteneur, quant à eux, décrivent comment les choses devraient fonctionner, sont conscients de Redux, distribuent des actions Redux directement pour effectuer des mutations et sont généralement générés par React-Redux. Ils sont souvent associés à un composant de présentation, fournissant ses accessoires.

Composants de présentation et composants de conteneur dans Redux.

Écrivons un composant de présentation et connectons-le à Redux via React-Redux:

const HelloWorld = ({ count, onButtonClicked }) => ( Hello! You've clicked the button {count} times! Click me ) HelloWorld.propTypes = { count: PropTypes.number.isRequired, onButtonClicked: PropTypes.func.isRequired, }

Notez qu'il s'agit d'un composant «stupide» qui repose entièrement sur ses accessoires pour fonctionner. C'est génial, car cela rend le Composant React facile à tester et à composer . Voyons comment connecter ce composant à Redux maintenant, mais voyons d'abord ce qu'est un composant d'ordre supérieur.

Composants d'ordre supérieur

React-Redux fournit une fonction d'assistance appelée connect( .. ) qui crée un composant d'ordre supérieur à partir d'un composant React «stupide» qui est conscient de Redux.

React met l'accent sur l'extensibilité et la réutilisation via la composition, c'est-à-dire lorsque vous enveloppez des composants dans d'autres composants. L'emballage de ces composants peut modifier leur comportement ou ajouter de nouvelles fonctionnalités. Voyons comment nous pouvons créer un composant d'ordre supérieur à partir de notre composant de présentation qui est conscient de Redux - un composant de conteneur.

Voici comment procéder:

import { connect } from 'react-redux' const mapStateToProps = state => { // state is the state of our store // return the props that we want to use for our component return { count: state.count, } } const mapDispatchToProps = dispatch => { // dispatch is our store dispatch function // return the props that we want to use for our component return { onButtonClicked: () => { dispatch({ type: 'BUTTON_CLICKED' }) }, } } // create our enhancer function const enhancer = connect(mapStateToProps, mapDispatchToProps) // wrap our 'dumb' component with the enhancer const HelloWorldContainer = enhancer(HelloWorld) // and finally we export it export default HelloWorldContainer

Notez que nous avons défini deux fonctions, mapStateToProps et mapDispatchToProps .

mapStateToProps est une fonction pure de (state: Object) qui renvoie un objet calculé à partir de l'état Redux. Cet objet sera fusionné avec les accessoires passés au composant encapsulé. Ceci est également connu sous le nom de sélecteur, car il sélectionne des parties de l'état Redux à fusionner dans les accessoires du composant.

mapDispatchToProps est également une fonction pure, mais l'une des (dispatch: (Action) => void) qui renvoie un objet calculé à partir de la fonction de répartition Redux. Cet objet sera également fusionné avec les accessoires passés au composant encapsulé.

Maintenant, pour utiliser notre composant conteneur, nous devons utiliser le Provider composant dans React-Redux pour indiquer au composant conteneur quel magasin utiliser:

import { Provider } from 'react-redux' import { render } from 'react-dom' import store from './store' // where ever your Redux store resides import HelloWorld from './HelloWorld' render( ( ), document.getElementById('container') )

Le Provider Le composant propage le magasin à tous les composants enfants qui s'abonnent au magasin Redux, gardant tout au même endroit et réduisant les points d'erreur ou de mutation!

Construire la confiance du code avec Redux

Avec cette nouvelle connaissance de Redux, ses nombreuses bibliothèques de support et sa connexion au framework avec React.js, vous pouvez facilement limiter le nombre de mutations dans votre application grâce au contrôle d'état. Un contrôle d'état solide, à son tour, vous permet de vous déplacer plus rapidement et de créer une base de code solide avec plus de confiance.

Véritable injection de dépendances avec les composants Symfony

Back-End

Véritable injection de dépendances avec les composants Symfony
Guide de synchronisation des données dans Microsoft SQL Server

Guide de synchronisation des données dans Microsoft SQL Server

Back-End

Articles Populaires
Explorer la fonctionnalité Get & Transform d'Excel
Explorer la fonctionnalité Get & Transform d'Excel
Comment aborder les wrappers pour les propriétés Swift
Comment aborder les wrappers pour les propriétés Swift
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Conception accessible vs conception inclusive (avec infographie)
Conception accessible vs conception inclusive (avec infographie)
 
L'intégration continue d'iOS avec le serveur Xcode expliquée
L'intégration continue d'iOS avec le serveur Xcode expliquée
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Webpack ou Browserify & Gulp: quel est le meilleur?
Webpack ou Browserify & Gulp: quel est le meilleur?
Business Analyst - Stratégie & Analytics
Business Analyst - Stratégie & Analytics
Articles Populaires
  • performances du nœud js vs java
  • le rappel n'est pas un nœud de fonction js
  • node js lève une nouvelle erreur
  • comment implémenter bootstrap en html
  • qu'est-ce que cac en finance
Catégories
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt