React.js View State Management Tutorial
Veröffentlicht: 2022-03-11Eines der größten und häufigsten Probleme bei der Front-End-Webentwicklung ist die Zustandsverwaltung. Freiberufliche Front-End-Entwickler wie ich konzentrieren sich ständig darauf, das Zustandsobjekt mit seiner Ansicht und der DOM-Darstellung synchron zu halten. Benutzer können auf vielfältige Weise mit der Anwendung interagieren, und es ist eine große Aufgabe, einen sauberen Übergang von einem Zustand in einen anderen bereitzustellen.
Ein bisschen Geschichte
Vor nicht allzu langer Zeit hatten Webanwendungen einen viel einfacheren Datenfluss. Der Browser würde eine Anfrage an den Server senden, die gesamte Anwendungslogik würde auf dem Server ausgeführt und eine vollständige HTML-Ansicht würde zur Präsentation für den Benutzer an den Browser zurückgesendet. Nachfolgende Benutzeraktionen (z. B. Klicks, Formularübermittlungen usw.) würden denselben Flow erneut auslösen. Anwendungen mussten sich nicht um den Benutzerstatus kümmern und jede Ansicht konnte neu generiert werden, indem eine neue Anfrage an den Server gesendet wurde.
Allerdings wuchsen Webanwendungen in ihrer Komplexität und auch die Benutzeranforderungen an UI/UX schritten voran. Das Neuladen der gesamten Seite, wenn sich nur ein Teil davon ändert, war ineffektiv und langsam. Wir brauchten eine schnelle, bissige und reaktionsschnelle Interaktion mit sofortiger Auswirkung auf die Benutzeroberfläche.
JavaScript kam zur Rettung. Entwickler begannen damit, erhebliche Mengen an Code zu schreiben, der im Browser ausgeführt wurde, bevor eine Anfrage an den Server gesendet wurde. jQuery brachte auch bedeutende Fortschritte für die Front-End-Webentwicklung, da es einfache und effektive Out-of-the-Box-Funktionen wie clientseitige Validierung, modale Fenster, Warnmeldungen, Animationen und sogar Ajax-basierte partielle Seitenaktualisierungen bereitstellte.
Komplexität verstehen
Schauen wir uns ein einfaches Beispiel für die Bewertung der Stärke eines Passworts an. Wenn das Passwort in Ordnung ist, sollte das Eingabefeld einen grünen Rand haben und eine nette Nachricht anzeigen. Wenn das Passwort schwach ist, sollte das Eingabefeld rot umrandet sein und eine Warnmeldung anzeigen. Wir zeigen möglicherweise auch ein Smiley-Gesicht an, wenn ein Passwort ausreichend stark ist.
Der folgende Code zeigt, wie dies durch DOM-Manipulation erfolgen könnte. Hier gibt es viele „wenn“ und der Code ist nicht sehr einfach zu lesen.
if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }
Wie oben gezeigt, müssen wir zuerst prüfen, ob der Benutzer überhaupt ein Passwort angegeben hat, und den Fall behandeln, in dem das Passwortfeld leer ist. Und in allen Fällen müssen wir sicherstellen, dass alle zugehörigen DOM-Elemente ordnungsgemäß aktualisiert werden. Dazu gehören die Nachricht, der Rahmen und das Smiley-Gesicht.
Unser Passwortfeld kann einen von drei Zuständen haben: leer, schwach oder stark. Und wie bereits erwähnt, haben wir drei verschiedene DOM-Elemente, die vom Zustand des Passwortfelds betroffen sind. Die Handhabung aller Kombinationen und die Sicherstellung, dass unsere Ansicht richtig angezeigt wird, erhöht die zyklomatische Komplexität selbst für einen einfachen Codeabschnitt wie diesen.
Das DOM arbeitet in einem beibehaltenen Modus , was bedeutet, dass es sich nur den aktuellen Zustand merkt. Um unsere Ansicht zu ändern, müssen wir Anweisungen für jedes DOM-Element bereitstellen und den Übergang programmieren.
Das Codieren von Übergängen anstelle von Zuständen kann komplex sein. Die Anzahl der Verzweigungen und Prüfungen, die wir in unserem Code durchführen müssen, wächst exponentiell mit der Anzahl der zu verwaltenden Ansichtszustände.
In unserem Beispiel haben wir drei Ansichtszustände definiert, was uns 3 * 2 = 6
Übergänge gegeben hat. Im Allgemeinen haben wir bei gegebenen N Zuständen N * (N - 1) = N^2 - N
Übergänge, die wir modellieren müssten. Denken Sie nur an die erhöhte Komplexität, wenn wir unserem Beispiel einen vierten Zustand hinzufügen.
Normalerweise gibt es zu viel Code, der sich auf die Modellierung der Übergänge bezieht. Es wäre viel besser, wenn wir nur unsere Ansichtszustände definieren könnten und uns nicht um alle Details des Übergangs von einem Zustand in einen anderen kümmern könnten.
Komplexität reduzieren
Angenommen, wir könnten den Ansichtszustand basierend auf dem Modellzustand deklarieren , anstatt den Übergang von einem Zustand in einen anderen explizit zu codieren, könnten wir so etwas haben:
var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }
Hier haben wir drei einfache Codezweige, die die drei möglichen Zustände unserer App darstellen. Wir geben nur die Spezifikation der Ansicht in jedem Zweig zurück, abhängig vom Modellstatus. Der gesamte DOM-Manipulationscode wird entfernt; wir liefern nur die Informationen darüber, was wir wollen, und nicht, wie wir dorthin gelangen.
Während dieser Ansatz die Codekomplexität erheblich reduziert, geht er auch davon aus, dass sich jemand oder etwas anderes in unserem Namen um die eigentliche DOM-Manipulation kümmert.
Hier kommt React ins Spiel. React stellt sicher, dass ein Ansichtsstatus basierend auf dem Status des zugrunde liegenden Datenmodells sofort verwaltet und aktualisiert wird.
Reaktion
React ist eine von Facebook erstellte JavaScript-Bibliothek. Es wurde entwickelt, um den UI-Teil von Webanwendungen zu handhaben. Sie können es sich als das V in der MVC-Architektur vorstellen. Es ist sehr konzentriert. Es macht keine Annahmen über den Rest Ihres Technologie-Stacks und behandelt nichts anderes als das Rendern von Komponenten. Es bietet keine Routing-Mechanismen, Modelle oder andere Funktionen, die normalerweise in größeren Frameworks gebündelt sind. Daher können Sie es mischen und mit jeder anderen Bibliothek oder jedem beliebigen Framework verwenden.
React ermöglicht es uns, UIs als Bäume zusammengesetzter Komponenten zu definieren. Ein React-Entwickler definiert diese Komponenten, indem er eine Renderfunktion angibt, die die Komponente bei gegebenem Eingabezustand beschreibt. Diese Funktion sollte rein sein (dh sie sollte keine Nebenwirkungen haben oder von irgendetwas anderem als ihrer expliziten Eingabe abhängen).
Die Render-Funktion gibt eine Ansichtsbeschreibung zurück, die React als Virtual DOM bezeichnet. Stellen Sie es sich als ein JavaScript-Objekt vor, das dem gerenderten DOM-Element entspricht.
Wenn Sie den Status der Komponente ändern, rendert sie einfach sich selbst und alle ihre untergeordneten Elemente neu und gibt ein neues Virtual DOM zurück.
Darüber hinaus führt React beim Übergang von einem Zustand in einen anderen keinen einfachen HTML-Ersatz durch. Es findet den Unterschied zwischen dem vorherigen und dem neuen Zustand und berechnet den effektivsten Satz von DOM-Operationen, um einen Übergang auszuführen.
Auch ohne die Leistung im Hinterkopf zu haben, ist die Reduzierung der Codekomplexität selbst erheblich und ermöglicht es uns, unsere Bemühungen auf die einzigartigeren und komplexeren Teile unserer Anwendung zu konzentrieren.
Um es etwas konkreter zu machen, so würde unser Tutorial-Beispiel mit React erstellt, um Ansichtszustände zu verwalten.
HINWEIS: Das folgende Codebeispiel ist im JSX-Präprozessor geschrieben, was eine gängige Methode zum Schreiben einer auf React basierenden Benutzeroberfläche ist.
function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body);
Die Emoji
Komponente, die gerendert wird, wenn die Passwortstärke mit <Emoji value="smiley" />
in Ordnung ist, ist nur eine weitere benutzerdefinierte Komponente (genau wie PasswordWithStrength
). Es ist so definiert:

var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });
React.js im Vergleich zu anderen
Fairerweise muss man jedoch sagen, dass es andere clientseitige JavaScript-Frameworks (wie Ember, Angular, Knockout und andere) gibt, die das Problem der Ansichtszustandsverwaltung ebenfalls gelöst und sogar weitere Funktionen hinzugefügt haben. Warum sollten Sie also React anstelle eines anderen Frameworks verwenden?
Es gibt zwei Hauptvorteile, die React im Vergleich zu den meisten anderen Bibliotheken hat.
Keine Datenbindung
Einige der anderen alternativen Frameworks verwenden Datenbindung, um DOM-Elemente Zustandseigenschaften zuzuordnen und sie durch Beobachtung der Eigenschaftsänderungen synchron zu halten. Dieser Ansatz ermöglicht das einmalige Rendern der Ansicht, wobei jede Änderung dann nur Änderungen der betroffenen DOM-Elemente auslöst. Andere Alternativen verwenden Dirty Checking ; Das heißt, anstatt einzelne Eigenschaftsänderungen zu beobachten, führen sie nur einen Unterschied zwischen dem vorherigen Zustand und dem neuen durch. React ist dem letzteren Ansatz ähnlicher, vergleicht aber statt Zustände die Ansichtsdarstellungen.
React hat keine Datenbindung. Von einem Entwickler wird erwartet, dass er die setState
-Methode aufruft oder die oberste Komponente neu rendert, wenn der Status geändert wird. Es umfasst einen Fluss in eine Richtung, vom Zustand zur Ansicht.
Dieses Konzept ist einfach zu übernehmen, da Entwickler im Allgemeinen nicht an die Datenbindung denken. Der Fokus liegt auf der visuellen Darstellung der Daten. Sie müssen sich daher keine Gedanken über abhängige Eigenschaften, Formatierung, Bindung spezieller HTML-Tags usw. machen. Mit React rendern Sie die Komponente einfach neu, wenn sich das Modell ändert.
Um den Unterschied in der Ansichtszustandsverwaltung hier zu verstehen, vergleichen wir Ember und React . Wir erstellen ein Objekt person
, das den vollständigen Namen in Großbuchstaben ausgibt. Nach zwei Sekunden simulieren wir die Änderung und aktualisieren die Ansicht.
// EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script>
Wir haben ein Objekt mit den Eigenschaften firstName
, lastName
und fullName
. Da Ember Eigenschaftsänderungen beobachtet, mussten wir angeben, dass fullName
von firstName
und lastName
. Dazu haben wir .property('firstName', 'lastName')
als wir den fullName
definiert haben.
Nach zwei Sekunden person.set('firstName', 'Harry');
wird ausgeführt. Dies löste die Aktualisierung der Ansicht und ihrer Bindung aus.
Jetzt machen wir dasselbe in React.
// EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000);
Obwohl der Ember-Code einfach und leicht zu lesen ist, ist es offensichtlich, dass React in seiner Einfachheit gewinnt. Die person
ist ein einfaches JavaScript-Objekt, wobei der fullName
einfach eine Funktion ist.
Keine Vorlagen
Jedes alternative Framework hat eine andere Art, mit Templates umzugehen. Einige von ihnen verwenden Zeichenfolgen, die in JavaScript kompiliert werden, während andere direkt DOM-Elemente verwenden. Die meisten von ihnen verwenden benutzerdefinierte HTML-Attribute und -Tags, die dann in HTML „kompiliert“ werden.
Vorlagen sind nicht Teil des JavaScript-Codes. Aus diesem Grund benötigt jede Alternative eine benutzerdefinierte Methode zur Darstellung allgemeiner Operationen, Bedingungen, Iterationen, Aufruffunktionen usw. Sie alle erzeugen am Ende eine neue Pseudosprache, die Entwickler lernen müssen.
In React gibt es keine Vorlagen, alles ist nur einfaches altes JavaScript.
React nutzt die volle Leistungsfähigkeit von JavaScript, um die Ansicht zu generieren. Die Rendermethode der Komponente ist eine JavaScript-Funktion.
JSX ist als Präprozessor verfügbar, der „HTML-ähnliche Syntax“ in normales JavaScript umwandelt, aber JSX ist optional und Sie können Standard-JavaScript ohne Präprozessoren verwenden. Sie können auch vorhandene JavaScript-Tools nutzen. Linters, Präprozessoren, Typanmerkungen, Minimierung, Eliminierung von totem Code usw.
Lassen Sie uns erneut ein konkretes Beispiel verwenden, um React mit einem der alternativen Frameworks für die Verwaltung von Ansichtszuständen zu vergleichen.
Das folgende Tutorial ist ein Beispiel für die Verwendung von AngularJS zum Auflisten von Hashtags und der Tweet-Anzahl für jeden von ihnen. Die Liste ist nach Anzahl sortiert und es wird eine Meldung angezeigt, wenn keine Hashtags vorhanden sind.
<!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div>
Um diese Liste erstellen zu können, muss ein Entwickler etwas über die AngularJS directives
, ng-show
und ng-repeat
lernen. Dann muss er etwas über AngularJS filters
lernen, um orderBy
zu verstehen. Viel Arbeit für eine einfache Sache wie die Ausgabe einer Liste!
Betrachten wir nun das React-Beispiel, das dasselbe tut:
// EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } }
Selbst wenn wir den „fortgeschritteneren“ Ansatz und JSX verwendet haben, kann jeder Webentwickler mit einem grundlegenden Verständnis von JavaScript den obigen Code leicht lesen und verstehen, was er tut. Standard-Bedingungsprüfung mit if
, Iteration mit map()
und ein standardmäßiges 'sort()' sind für jeden Entwickler selbstverständlich, sodass keine zusätzliche Syntax oder andere Konzepte zu lernen sind.
Fazit
Die wichtigste Erkenntnis aus diesem React.js-Tutorial ist die Tatsache, dass React es Ihnen ermöglicht, sich auf die eigentliche Ansichtszustandsverwaltung statt auf Übergänge zu konzentrieren, wodurch Ihre Arbeit und Ihre Anwendung vereinfacht werden.
Die Lernkurve für die Einführung von React ist ziemlich trivial. Keine benutzerdefinierte Vorlagensprache zu beherrschen, keine Datenbindung, um die man sich Sorgen machen muss, und alles läuft auf JavaScript-Funktionen hinaus, die UI-Elemente beschreiben.
Um mehr über die Vereinfachung Ihres Anwendungscodes mit React zu erfahren, werfen Sie einen Blick auf diesen Vortrag von Steven Luscher, Decomplexifying Code with React.
Hier ist etwas zusätzliche Lektüre für alle, die den nächsten Schritt machen und mit der Verwendung von React beginnen möchten:
- http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome