React.js Vizualizați tutorialul de gestionare a stării

Publicat: 2022-03-11

Una dintre cele mai mari și mai frecvente probleme în dezvoltarea web front-end este managementul de stat. Dezvoltatorii front-end independenți, ca mine, sunt concentrați în mod constant pe menținerea obiectului de stat în sincronizare cu vizualizarea sa și reprezentarea DOM. Utilizatorii pot interacționa cu aplicația în multe moduri și este o sarcină mare să ofere o tranziție curată de la o stare la alta.

Cu acest tutorial React.js, aflați mai multe despre gestionarea stării vizualizării.

Un pic de istorie

Nu cu mult timp în urmă, aplicațiile web aveau un flux de date mult mai simplu. Browserul ar trimite o solicitare către server, toată logica aplicației ar fi executată pe server și o vizualizare HTML completă ar fi trimisă înapoi către browser pentru prezentare utilizatorului. Acțiunile ulterioare ale utilizatorului (cum ar fi clicuri, trimiteri de formulare etc.) ar declanșa din nou același flux. Aplicațiile nu trebuiau să-și facă griji cu privire la starea utilizatorului și fiecare vizualizare putea fi regenerată prin trimiterea unei noi cereri către server.

Cu toate acestea, aplicațiile web au crescut în complexitate, iar cerințele utilizatorilor față de UI/UX au avansat și ele. Reîncărcarea întregii pagini, când doar o parte din ea se schimbă, a fost ineficientă și lentă. Aveam nevoie de o interacțiune rapidă, rapidă și receptivă, cu impact imediat asupra interfeței de utilizare.

JavaScript a venit în ajutor. Dezvoltatorii au început să scrie cantități semnificative de cod care au fost executate în browser înainte ca o solicitare să fie trimisă către server. jQuery a adus, de asemenea, progrese semnificative în dezvoltarea web front-end, deoarece a furnizat capabilități simple și eficiente, cum ar fi validarea clientului, ferestre modale, mesaje de alertă, animații și chiar actualizări parțiale ale paginilor bazate pe Ajax.

Înțelegerea complexității

Să aruncăm o privire la un exemplu simplu de evaluare a puterii unei parole. Dacă parola este OK, caseta de introducere ar trebui să aibă un chenar verde și ar trebui să afișeze un mesaj frumos. Dacă parola este slabă, caseta de introducere ar trebui să aibă un chenar roșu și ar trebui să afișeze un mesaj de avertizare. De asemenea, am putea arăta o față zâmbitoare atunci când o parolă este suficient de puternică.

Următorul cod demonstrează cum se poate face acest lucru prin manipularea DOM. Există o mulțime de „dacă” aici, iar codul nu este foarte ușor de citit.

 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(); } } }

După cum se arată mai sus, mai întâi trebuie să verificăm dacă utilizatorul a furnizat vreo parolă și să ne ocupăm de cazul în care câmpul pentru parolă este gol. Și în toate cazurile, trebuie să ne asigurăm că toate elementele DOM aferente sunt actualizate corect. Aceasta include mesajul, chenarul și fața zâmbitoare.

Câmpul nostru de parolă poate fi în una dintre cele trei stări: gol, slab sau puternic. Și după cum am menționat, avem trei elemente DOM diferite care sunt afectate de starea câmpului parolei. Gestionarea tuturor combinațiilor și asigurarea faptului că vizualizarea noastră este afișată corect, crește complexitatea ciclomatică chiar și pentru o simplă bucată de cod ca aceasta.

DOM funcționează într-un mod reținut , ceea ce înseamnă că își amintește doar starea curentă. Pentru a ne modifica vizualizarea, trebuie să oferim instrucțiuni pentru fiecare element DOM și să programăm tranziția.

Codarea tranzițiilor în loc de stări poate fi complexă. Numărul de ramuri și verificări pe care trebuie să le efectuăm în codul nostru crește exponențial odată cu numărul de stări de vizualizare de gestionat.

În exemplul nostru, am definit trei stări de vedere, care ne-au dat 3 * 2 = 6 tranziții. În general, având în vedere N stări, avem N * (N - 1) = N^2 - N tranziții pe care ar trebui să le modelăm. Gândiți-vă doar la complexitatea crescută dacă am adăuga o a patra stare exemplului nostru.

De obicei, există prea mult cod legat de modelarea tranzițiilor . Ar fi mult mai bine dacă ne-am putea defini doar stările de vedere și să nu ne facem griji pentru toate detaliile tranziției de la o stare la alta.

Reducerea complexității

Presupunând că am putea declara starea de vizualizare pe baza stării modelului, în loc să codificăm în mod explicit tranziția de la o stare la alta, am putea avea ceva de genul acesta:

 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'}) ); }

Aici avem trei ramuri simple de cod, reprezentând cele trei stări posibile ale aplicației noastre. Doar returnăm specificația vizualizării în fiecare ramură, în funcție de starea modelului. Tot codul de manipulare DOM este eliminat; furnizăm doar informații despre ceea ce ne dorim, nu cum să ajungem acolo.

Deși această abordare reduce în mod semnificativ complexitatea codului, presupune, de asemenea, că există cineva sau altceva care să se ocupe de manipularea DOM reală în numele nostru.

Aici intervine React. React se va asigura că o stare de vizualizare este imediat gestionată și actualizată pe baza stării modelului de date subiacent.

Reacţie

React este o bibliotecă JavaScript creată de Facebook. Este conceput pentru a gestiona partea UI a aplicațiilor web. Vă puteți gândi la el ca V-ul în arhitectura MVC. Este foarte concentrat. Nu face presupuneri cu privire la restul stivei dvs. de tehnologie și nu se ocupă de nimic altceva decât randarea componentelor. Nu oferă mecanisme de rutare, modele sau alte caracteristici care sunt de obicei grupate în cadre mai mari. Astfel, îl puteți amesteca și utiliza cu orice altă bibliotecă sau cadru dorit.

React ne permite să definim interfețele de utilizator ca arbori de componente compozite. Un dezvoltator React definește acele componente specificând o funcție de randare care descrie componenta, având în vedere starea de intrare. Această funcție ar trebui să fie pură (adică, nu ar trebui să aibă niciun efect secundar sau să depindă de altceva decât de intrarea sa explicită).

Funcția de randare returnează o descriere a vizualizării, pe care React o numește un DOM virtual . Gândiți-vă la el ca la un obiect JavaScript corespunzător elementului DOM redat.

Când modificați starea componentei, aceasta se redă din nou pe sine și toate elementele sale secundare, returnând un nou Virtual DOM .

Mai mult, React nu va face o simplă înlocuire HTML, atunci când trece de la o stare la alta. Acesta va găsi diferența dintre starea anterioară și cea nouă și va calcula cel mai eficient set de operații DOM pentru a executa o tranziție.

Chiar și fără a ține cont de performanță, reducerea complexității codului este în sine semnificativă și ne permite să ne concentrăm eforturile asupra părților mai unice și mai complexe ale aplicației noastre.

Pentru a deveni puțin mai concret, acesta este modul în care exemplul nostru tutorial ar fi realizat folosind React pentru a gestiona stările de vizualizare.

NOTĂ: Următorul exemplu de cod este scris în preprocesorul JSX, care este o modalitate obișnuită de a scrie o interfață de utilizare bazată pe React.

 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);

Componenta Emoji care este redată atunci când puterea parolei este OK cu <Emoji value="smiley" /> este doar o altă componentă personalizată (la fel ca PasswordWithStrength ). Este definit astfel:

 var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });

React.js vs. Alții

În mod corect, totuși, există și alte cadre JavaScript la nivelul clientului (cum ar fi Ember, Angular, Knockout și altele) care au rezolvat și problema de gestionare a stării vizualizării și chiar i-au adăugat mai multe funcții. Deci, de ce ați dori să utilizați React în loc de orice alt cadru?

Există două avantaje cheie pe care le are React, în comparație cu majoritatea celorlalte biblioteci.

Fără legare de date

Unele dintre celelalte cadre alternative folosesc legarea de date pentru a mapa elementele DOM la proprietățile de stat și le mențin sincronizate observând modificările proprietăților. Această abordare permite redarea vizualizării o singură dată, fiecare modificare declanșând apoi doar modificări ale elementelor DOM afectate. Alte alternative folosesc verificarea murdară ; adică, în loc să observe modificări ale proprietăților individuale, ei doar efectuează o diferență între starea anterioară și cea nouă. React este mai asemănător cu cea de-a doua abordare, dar, în loc să compare stările, compară reprezentările vizualizării.

React nu are legături de date. Se așteaptă ca un dezvoltator să apeleze metoda setState sau să redea componenta de sus, atunci când starea este schimbată. Ea îmbrățișează un flux unidirecțional, de la stat la vedere.

Acest concept este ușor de adoptat, deoarece dezvoltatorii în general nu se gândesc la legarea datelor. Accentul se pune pe reprezentarea vizuală a datelor. Prin urmare, nu trebuie să vă gândiți la proprietăți dependente, formatare, legarea etichetelor HTML speciale etc. Cu React, pur și simplu redați din nou componenta atunci când modelul se schimbă.

Pentru a înțelege diferența de gestionare a stării de vedere aici, să comparăm Ember și React . Vom crea o person obiect care va scoate numele complet în majuscule. După două secunde, vom simula schimbarea și vom actualiza vizualizarea.

 // 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>

Am creat un obiect cu firstName , lastName și fullName . Deoarece Ember observă modificările proprietăților, a trebuit să specificăm că fullName depinde de firstName și lastName . Pentru a face acest lucru, am adăugat .property('firstName', 'lastName') când am definit fullName .

După două secunde, person.set('firstName', 'Harry'); este executat. Acest lucru a declanșat actualizarea vizualizării și legarea acesteia.

Acum să facem același lucru în 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);

Chiar dacă codul Ember este simplu și ușor de citit, este evident că React câștigă prin simplitate. person este un obiect JavaScript simplu, cu fullName pur și simplu o funcție.

Fără șabloane

Fiecare cadru alternativ are un mod diferit de a gestiona șabloanele. Unele dintre ele folosesc șiruri care sunt compilate în JavaScript, în timp ce altele folosesc elemente DOM direct. Cele mai multe dintre ele folosesc atribute și etichete HTML personalizate care sunt apoi „compilate” în HTML.

Șabloanele nu fac parte din codul JavaScript. Din acest motiv, fiecare alternativă are nevoie de o modalitate personalizată de a reprezenta operațiuni comune, condiționale, iterații, funcții de apelare etc. Toate ajung să creeze un nou pseudo-limbaj pe care dezvoltatorii trebuie să-l învețe.

Nu există șabloane în React, totul este pur și simplu JavaScript vechi.

React folosește toată puterea JavaScript pentru a genera vizualizarea. Metoda de randare a componentei este o funcție JavaScript.

JSX este disponibil ca un preprocesor care transformă „sintaxa asemănătoare HTML” în JavaScript normal, dar JSX este opțional și sunteți liber să utilizați JavaScript standard fără preprocesoare. De asemenea, puteți profita de instrumentele JavaScript existente. Linters, preprocesoare, adnotări de tip, minificare, eliminare cod mort etc.

Să folosim din nou un exemplu concret pentru a compara React cu unul dintre cadrele alternative pentru managementul stării vizualizării.

Următorul tutorial este un exemplu de utilizare a AngularJS pentru a lista hashtag-uri și numărul de tweet-uri pentru fiecare dintre ele. Lista este sortată după număr și este afișat un mesaj dacă nu există hashtag-uri.

 <!-- 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>

Pentru a putea face această listă, un dezvoltator trebuie să învețe despre AngularJS directives , ng-show și ng-repeat . Apoi trebuie să învețe despre AngularJS filters pentru a înțelege orderBy . Multă muncă pentru un lucru simplu, cum ar fi scoaterea unei liste!

Acum să luăm în considerare exemplul React care face același lucru:

 // 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> } }

Chiar dacă am folosit abordarea „mai avansată” și JSX, fiecare dezvoltator web cu o înțelegere de bază a JavaScript poate citi cu ușurință codul de mai sus și poate înțelege ce face. Verificarea condiționată standard folosind if , iterația folosind map() și un „sort()” standard vine în mod natural pentru orice dezvoltator, deci nu există o sintaxă suplimentară sau alte concepte de învățat.

Concluzie

Principala concluzie din acest tutorial React.js este faptul că React vă permite să vă concentrați pe gestionarea stării de vizualizare efectivă, mai degrabă decât pe tranziții, simplificându-vă astfel munca și aplicația.

Curba de învățare pentru adoptarea React este destul de banală. Fără limbaj de șablon personalizat de stăpânit, nicio legătură de date de care să vă faceți griji și totul se reduce la funcții JavaScript care descriu elementele UI.

Pentru a afla mai multe despre simplificarea codului aplicației folosind React, aruncați o privire la această discuție a lui Steven Luscher, Decomplexifying Code with React.

Iată câteva lecturi suplimentare pentru oricine dorește să facă următorul pas și să înceapă să folosească React:

  • http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome