React.js Visualizza il tutorial sulla gestione dello stato

Pubblicato: 2022-03-11

Uno dei problemi più grandi e comuni nello sviluppo web front-end è la gestione dello stato. Gli sviluppatori front-end freelance come me sono costantemente concentrati sul mantenere l'oggetto di stato sincronizzato con la sua vista e la rappresentazione DOM. Gli utenti possono interagire con l'applicazione in molti modi ed è un compito arduo fornire una transizione pulita da uno stato all'altro.

Con questo tutorial di React.js, scopri di più sulla gestione dello stato di visualizzazione.

Un po' di storia

Non molto tempo fa, le applicazioni web avevano un flusso di dati molto più semplice. Il browser invierebbe una richiesta al server, tutta la logica dell'applicazione verrebbe eseguita sul server e una visualizzazione HTML completa verrebbe restituita al browser per la presentazione all'utente. Le successive azioni dell'utente (come clic, invii di moduli e così via) attiverebbero nuovamente lo stesso flusso. Le applicazioni non dovevano preoccuparsi dello stato dell'utente e ciascuna vista poteva essere rigenerata inviando una nuova richiesta al server.

Tuttavia, le applicazioni Web sono cresciute nella loro complessità e anche le richieste degli utenti dell'interfaccia utente/UX stavano avanzando. Ricaricare l'intera pagina, quando cambia solo una parte, è stato inefficace e lento. Avevamo bisogno di un'interazione rapida, scattante e reattiva con un impatto immediato sull'interfaccia utente.

JavaScript è venuto in soccorso. Gli sviluppatori hanno iniziato a scrivere quantità significative di codice che è stato eseguito nel browser prima che una richiesta fosse inviata al server. jQuery ha anche apportato progressi significativi allo sviluppo Web front-end, in quanto ha fornito funzionalità pronte all'uso semplici ed efficaci come la convalida lato client, finestre modali, messaggi di avviso, animazioni e persino aggiornamenti parziali di pagina basati su Ajax.

Capire la complessità

Diamo un'occhiata a un semplice esempio di valutazione della forza di una password. Se la password è OK, la casella di input dovrebbe avere un bordo verde e dovrebbe visualizzare un bel messaggio. Se la password è debole, la casella di immissione dovrebbe avere un bordo rosso e dovrebbe visualizzare un messaggio di avviso. Potremmo anche mostrare una faccina sorridente quando una password è sufficientemente forte.

Il codice seguente mostra come ciò potrebbe essere eseguito dalla manipolazione DOM. Ci sono molti "se" qui e il codice non è molto facile da leggere.

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

Come mostrato sopra, per prima cosa dobbiamo verificare se l'utente ha fornito una password e gestire il caso in cui il campo della password è vuoto. E in tutti i casi, dobbiamo assicurarci che tutti gli elementi DOM correlati siano aggiornati correttamente. Ciò include il messaggio, il bordo e la faccina sorridente.

Il nostro campo password può essere in uno dei tre stati: vuoto, debole o forte. E come notato, abbiamo tre diversi elementi DOM che sono interessati dallo stato del campo password. Gestire tutte le combinazioni e assicurarsi che la nostra vista sia visualizzata correttamente, aumenta la complessità ciclomatica anche per un semplice pezzo di codice come questo.

Il DOM funziona in modalità trattenuta , il che significa che ricorda solo lo stato corrente. Per modificare la nostra visualizzazione, dobbiamo fornire istruzioni per ogni elemento DOM e programmare la transizione.

Le transizioni di codifica invece degli stati possono essere complesse. Il numero di rami e controlli che dobbiamo eseguire nel nostro codice cresce esponenzialmente con il numero di stati di visualizzazione da gestire.

Nel nostro esempio, abbiamo definito tre stati di visualizzazione, che ci hanno dato 3 * 2 = 6 transizioni. In generale, dati N stati, abbiamo N * (N - 1) = N^2 - N transizioni che dovremmo modellare. Pensa solo alla maggiore complessità se aggiungiamo un quarto stato al nostro esempio.

In genere c'è troppo codice relativo alla modellazione delle transizioni . Sarebbe molto meglio se potessimo semplicemente definire i nostri stati di vista e non preoccuparci di tutti i dettagli della transizione da uno stato all'altro.

Ridurre la complessità

Supponendo di poter dichiarare lo stato di visualizzazione in base allo stato del modello, invece di codificare esplicitamente la transizione da uno stato all'altro, potremmo avere qualcosa del genere:

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

Qui abbiamo tre semplici rami di codice, che rappresentano i tre possibili stati della nostra app. Restituiamo semplicemente la specifica della vista in ogni ramo, a seconda dello stato del modello. Tutto il codice di manipolazione del DOM viene rimosso; forniamo solo le informazioni su ciò che vogliamo e non su come arrivarci.

Sebbene questo approccio riduca significativamente la complessità del codice, presuppone anche che ci sia qualcuno o qualcos'altro che si occupi dell'effettiva manipolazione del DOM per nostro conto.

È qui che entra in gioco React. React si assicurerà che uno stato di visualizzazione venga immediatamente gestito e aggiornato in base allo stato del modello di dati sottostante.

Reazione

React è una libreria JavaScript creata da Facebook. È progettato per gestire la parte dell'interfaccia utente delle applicazioni Web. Puoi pensarlo come la V nell'architettura MVC. È molto concentrato. Non fa ipotesi sul resto del tuo stack tecnologico e non gestisce nient'altro che il rendering dei componenti. Non fornisce alcun meccanismo di routing, modello o altre funzionalità che di solito sono raggruppate in framework più grandi. Pertanto, puoi mescolarlo e usarlo con qualsiasi altra libreria o framework che desideri.

React ci consente di definire le UI come alberi di componenti compositi. Uno sviluppatore React definisce quei componenti specificando una funzione di rendering che descrive il componente, dato lo stato di input. Quella funzione dovrebbe essere pura (cioè, non dovrebbe avere alcun effetto collaterale o dipendere da qualcosa di diverso dal suo input esplicito).

La funzione di rendering restituisce una descrizione della vista, che React chiama un Virtual DOM . Pensalo come un oggetto JavaScript corrispondente all'elemento DOM renderizzato.

Quando modifichi lo stato del componente, esegue nuovamente il rendering di se stesso e di tutti i suoi elementi figlio, restituendo un nuovo Virtual DOM .

Inoltre, React non eseguirà una semplice sostituzione dell'HTML durante la transizione da uno stato all'altro. Troverà la differenza tra il precedente e il nuovo stato e calcolerà l'insieme più efficace di operazioni DOM per eseguire una transizione.

Anche senza tenere conto delle prestazioni, la riduzione della complessità del codice è di per sé significativa e ci consente di concentrare i nostri sforzi sulle parti più uniche e complesse della nostra applicazione.

Per essere un po' più concreti, ecco come verrebbe realizzato il nostro esempio di tutorial utilizzando React per gestire gli stati di visualizzazione.

NOTA: il seguente codice di esempio è scritto nel preprocessore JSX, che è un modo comune per scrivere un'interfaccia utente basata su 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);

Il componente Emoji che viene visualizzato quando la sicurezza della password è OK con <Emoji value="smiley" /> è solo un altro componente personalizzato (proprio come PasswordWithStrength ). Si definisce così:

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

React.js contro altri

In tutta onestà, tuttavia, ci sono altri framework JavaScript lato client (come Ember, Angular, Knockout e altri) che hanno risolto anche il problema di gestione dello stato di visualizzazione e hanno persino aggiunto più funzionalità. Quindi, perché dovresti usare React invece di qualsiasi altro framework?

Ci sono due vantaggi chiave che React ha, rispetto alla maggior parte delle altre librerie.

Nessun vincolo dati

Alcuni degli altri framework alternativi utilizzano il data binding per mappare gli elementi DOM alle proprietà dello stato e mantenerli sincronizzati osservando le modifiche alle proprietà. Questo approccio consente di eseguire il rendering della vista una volta, con ogni modifica che attiva solo le modifiche degli elementi DOM interessati. Altre alternative utilizzano il controllo sporco ; cioè, invece di osservare i cambiamenti delle singole proprietà, eseguono semplicemente una differenza tra lo stato precedente e quello nuovo. React è più simile a quest'ultimo approccio ma, invece di confrontare gli stati, confronta le rappresentazioni della vista.

React non ha data binding. Uno sviluppatore deve chiamare il metodo setState o eseguire nuovamente il rendering del componente principale quando lo stato viene modificato. Abbraccia un flusso unidirezionale, dallo stato alla vista.

Questo concetto è facile da adottare poiché gli sviluppatori generalmente non pensano al data binding. Il focus è sulla rappresentazione visiva dei dati. Pertanto non è necessario pensare a proprietà dipendenti, formattazione, associazione di tag HTML speciali, ecc. Con React, è sufficiente eseguire nuovamente il rendering del componente quando il modello cambia.

Per capire la differenza nella gestione dello stato di visualizzazione qui, confrontiamo Ember e React . Creeremo una person oggetto che produrrà il nome completo in maiuscolo. Dopo due secondi, simuleremo la modifica e aggiorneremo la vista.

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

Abbiamo creato un oggetto con le proprietà firstName , lastName e fullName . Poiché Ember sta osservando le modifiche alle proprietà, abbiamo dovuto specificare che fullName dipende da firstName e lastName . Per fare ciò abbiamo aggiunto .property('firstName', 'lastName') quando abbiamo definito fullName .

Dopo due secondi, person.set('firstName', 'Harry'); viene eseguito. Ciò ha attivato l'aggiornamento della vista e la sua associazione.

Ora facciamo lo stesso 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);

Anche se il codice Ember è semplice e facile da leggere, è ovvio che React vince nella semplicità. La person è un semplice oggetto JavaScript, con fullName semplicemente una funzione.

Nessun modello

Ogni framework alternativo ha un modo diverso di gestire i modelli. Alcuni di loro usano stringhe che vengono compilate in JavaScript, mentre altri usano direttamente gli elementi DOM. La maggior parte di essi utilizza attributi e tag HTML personalizzati che vengono poi "compilati" in HTML.

I modelli non fanno parte del codice JavaScript. Per questo motivo, ogni alternativa ha bisogno di un modo personalizzato per rappresentare operazioni comuni, condizionali, iterazioni, funzioni di chiamata, ecc. Tutti finiscono per creare un nuovo pseudo-linguaggio che gli sviluppatori devono imparare.

Non ci sono modelli in React, tutto è semplicemente un vecchio JavaScript.

React utilizza tutta la potenza di JavaScript per generare la vista. Il metodo di rendering del componente è una funzione JavaScript.

JSX è disponibile come preprocessore che trasforma la "sintassi simile a HTML" in JavaScript normale, ma JSX è opzionale e sei libero di utilizzare JavaScript standard senza preprocessori. Puoi anche sfruttare gli strumenti JavaScript esistenti. Linter, preprocessori, annotazioni di tipo, minimizzazione, eliminazione di codici morti, ecc.

Usiamo ancora un esempio concreto per confrontare React con uno dei framework alternativi per la gestione dello stato di visualizzazione.

Il seguente tutorial è un esempio di utilizzo di AngularJS per elencare gli hashtag e il conteggio dei tweet per ciascuno di essi. L'elenco è ordinato per conteggio e viene visualizzato un messaggio se non sono presenti hashtag.

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

Per poter creare questo elenco, uno sviluppatore deve conoscere le AngularJS directives , ng-show e ng-repeat . Quindi ha bisogno di conoscere i AngularJS filters per capire orderBy . Un sacco di lavoro per una cosa semplice come l'output di una lista!

Consideriamo ora l'esempio React che fa la stessa cosa:

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

Anche se abbiamo utilizzato l'approccio "più avanzato" e JSX, ogni sviluppatore web con una conoscenza di base di JavaScript può facilmente leggere il codice sopra e capire cosa fa. Il controllo condizionale standard usando if , l'iterazione usando map() e uno standard 'sort()' viene naturalmente a qualsiasi sviluppatore, quindi non c'è sintassi aggiuntiva o altri concetti da imparare.

Conclusione

L'aspetto principale di questo tutorial di React.js è il fatto che React ti consente di concentrarti sull'effettiva gestione dello stato di visualizzazione piuttosto che sulle transizioni, semplificando così il tuo lavoro e la tua applicazione.

La curva di apprendimento per l'adozione di React è abbastanza banale. Nessun linguaggio di template personalizzato da padroneggiare, nessun data binding di cui preoccuparsi e tutto si riduce alle funzioni JavaScript che descrivono gli elementi dell'interfaccia utente.

Per saperne di più sulla semplificazione del codice dell'applicazione utilizzando React, dai un'occhiata a questo intervento di Steven Luscher, Decomplexifying Code with React.

Ecco alcune letture aggiuntive per chiunque voglia fare il passo successivo e iniziare a usare React:

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