2014-05-10 5 views
64

Ho letto su React per qualche giorno. Posso capire la maggior parte di ciò che sto guardando, ma non sono del tutto fiducioso nella mia capacità di scriverlo. Ho lavorato su una piccola web app che fa tutta la sua generazione in html attraverso jQuery e aggiungendo elementi l'uno all'altro. Mi piacerebbe provare a ricostruirlo con React perché credo che sarebbe più veloce. Questo JSFiddle è un piccolo esempio del tipo di cose su cui sto lavorando. Come lo scriverebbe con React?Come passare da jQuery a React.js?

JS:

function remove() { 
    this.remove(); 
} 

function timed_append_new_element() { 
    setTimeout(function() { 
     var added_content = $("<span />", { 
      class: "added_content", 
      text: "Click to close", 
      click: remove 
     }); 
     container.append(added_content); 
    }, 3000); 
} 

function append_new_element() { 
    var added_content = $("<span />", { 
     class: "timed_added_content", 
     text: "Click to close", 
     click: remove 
    }); 
    container.append(added_content); 
} 


var container = $("<div />", { 
    class: "container" 
}); 
var header = $("<span />", { 
    class: "header", 
    text: "jQuery to React.js Header" 
}); 
var add_button = $("<button />", { 
    class: "add_button", 
    text: "Add Element", 
    click: append_new_element 
}); 
var timed_add_button = $("<button />", { 
    class: "add_button", 
    text: "Add Element in 3 seconds", 
    click: timed_append_new_element 
}); 
container.append(header); 
container.append(add_button); 
container.append(timed_add_button); 
$("body").append(container); 

risposta

161

Ci sono alcuni principi fondamentali da tenere a mente che possono aiutare a costruire una buona applicazione Reagire:

l'interfaccia utente dovrebbe essere una funzione dei dati

In molte applicazioni in stile "jQuery soup", la logica di business per l'applicazione, i dati dell'app e il codice di interazione dell'interfaccia utente sono tutti mescolati. Ciò rende difficile eseguire il debug di questo tipo di applicazioni e, soprattutto, è difficile crescere. Reagire, come molti moderni framework applicativi lato client, impone l'idea che l'interfaccia utente sia solo una rappresentazione dei tuoi dati. Se si desidera modificare l'interfaccia utente, è necessario modificare una parte di dati e consentire a qualsiasi sistema di binding utilizzato dal framework per aggiornare l'interfaccia utente.

In React, ciascun componente è (idealmente) una funzione di due pezzi di dati-le proprietà passati alla istanza del componente, e lo stato che il componente gestisce internamente. Date le stesse proprietà (o "oggetti di scena") e lo stato, il componente dovrebbe renderizzare allo stesso modo.

Questa può essere un'idea un po 'astratta senza esempi concreti, quindi tienilo a mente mentre andiamo avanti per ora.

Non toccare il DOM

In Reagire, ancor più che altri framework associati ai dati, si dovrebbe cercare di non manipolare direttamente, se possibile il DOM. Molte delle prestazioni e delle caratteristiche di complessità di React sono possibili solo perché React utilizza un DOM virtuale con algoritmi difffici internamente per operare sul DOM reale. Ogni volta che costruisci un componente che raggiunge e fa la propria manipolazione DOM, dovresti chiederti se puoi creare la stessa caratteristica in modo più idiomatico con le funzioni DOM virtuali di React.

Ovviamente, a volte è necessario accedere al DOM, o si vorrà incorporare qualche plugin jQuery senza ricostruirlo in React. Per tempi come questi, React ti dà il buon component lifecycle hooks che puoi usare per assicurarti che le prestazioni di React non ne risentano troppo (o, in alcuni casi, per impedire che il tuo componente si rompa).

La mancata manipolazione del DOM va di pari passo con "UI in funzione dei dati" sopra.

invertito il flusso di dati

In un'applicazione di grandi dimensioni React, può essere difficile tenere traccia di quali sub-componente gestisce un certo tipo di dati applicativi. Per questo motivo, il team di React consiglia di mantenere la logica di manipolazione dei dati in una posizione centrale. Il modo più semplice per farlo è passare i callback nei componenti figlio; c'è anche un'architettura sviluppata su Facebook chiamata Flux che ha lo its own website.

Creare componenti componibili

Un sacco di volte, si può essere tentati di scrivere una grande componente che gestisce diversi pezzi di Stato e di diversi pezzi di logica dell'interfaccia utente. Laddove possibile (ed entro limiti ragionevoli), dovresti prendere in considerazione la possibilità di rompere i componenti più grandi in quelli più piccoli che operano su un singolo pezzo di dati o sulla logica dell'interfaccia utente. Ciò rende molto più semplice estendere e spostare i pezzi della tua applicazione.

Attenzione dati mutabili

Dal stato componente deve essere aggiornato solo attraverso chiamate al this.setState dal all'interno del componente, è utile a diffidare dei dati mutabili. Questo è doppiamente vero quando più funzioni (o componenti!) Potrebbero aggiornare l'oggetto mutabile nella stessa spunta; React potrebbe tentare di modificare lo stato in batch e potresti perdere gli aggiornamenti! Come menzionato nei commenti di Eliseu Monar, considera la possibilità di clonare oggetti mutabili prima di mutarli. React ha immutability helpers che può aiutare.

Un'altra opzione è quella di rinunciare a mantenere le strutture di dati mutabili direttamente nello stato; il modello Flux, menzionato sopra, è un'interpretazione interessante di questa idea.


C'è un grande articolo sul sito Reagire chiamato Thinking in React che va su come si potrebbe prendere un'idea o un mockup e trasformarlo in un'applicazione Reagire, e incoraggiare fortemente di andare su di esso. Come esempio concreto, diamo un'occhiata al codice che hai fornito. Essenzialmente hai una parte di dati da gestire: un elenco di contenuti che esiste all'interno dell'elemento container. Tutte le modifiche all'interfaccia utente possono essere rappresentate da aggiunte, rimozioni e modifiche a tali dati.

Applicando i principi sopra, l'applicazione finale potrebbe essere simile a questa:

/** @jsx React.DOM */ 

var Application = React.createClass({ 
    getInitialState: function() { 
    return { 
     content: [] 
    }; 
    }, 

    render: function() { 
    return (
     <div className="container"> 
     <span className="header">jQuery to React.js Header</span> 
     <button className="add_button" 
       onClick={this.addContent}>Add Element</button> 
     <button className="add_button" 
       onClick={this.timedAddContent}>Add Element in 3 Seconds</button> 
     {this.state.content.map(function(content) { 
      return <ContentItem content={content} removeItem={this.removeItem} />; 
     }.bind(this))} 
     </div> 
    ); 
    }, 

    addContent: function() { 
    var newItem = {className: "added_content", text: "Click to close"}, 
     content = this.state.content, 
     newContent = React.addons.update(content, {$push: [newItem]}); 
    this.setState({content: newContent}); 
    }, 

    timedAddContent: function() { 
    setTimeout(function() { 
     var newItem = {className: "timed_added_content", text: "Click to close"}, 
      content = this.state.content, 
      newContent = React.addons.update(content, {$push: [newItem]}); 
     this.setState({content: newContent}); 
    }.bind(this), 3000); 
    }, 

    removeItem: function(item) { 
    var content = this.state.content, 
     index = content.indexOf(item); 
    if (index > -1) { 
     var newContent = React.addons.update(content, {$splice: [[index, 1]]}); 
     this.setState({content: newContent}); 
    } 
    } 
}); 

var ContentItem = React.createClass({ 
    propTypes: { 
    content: React.PropTypes.object.isRequired, 
    removeItem: React.PropTypes.func.isRequired 
    }, 

    render: function() { 
    return <span className={this.props.content.className} 
       onClick={this.onRemove}>{this.props.content.text}</span>; 
    }, 

    onRemove: function() { 
    this.props.removeItem(this.props.content); 
    } 
}); 

React.renderComponent(<Application />, document.body); 

È possibile visualizzare il codice in azione in this JSFiddle: http://jsfiddle.net/BinaryMuse/D59yP/

L'applicazione è costituita da due componenti: un componente di primo livello chiamato Application, che gestisce (nel suo stato) un array chiamato content e un componente chiamato ContentItem, che rappresenta l'interfaccia utente e il comportamento di un singolo elemento da tale array. Il metodo Application di render restituisce un elemento ContentItem per ciascun elemento dell'array di contenuto.

Una cosa da notare è che tutta la logica per la gestione dei valori all'interno dell'array content viene gestita nel componente Application; i componenti ContentItem vengono passati un riferimento metodo Application s' removeItem che i ContentItem delegati quando selezionato. Ciò mantiene tutta la logica per la manipolazione dello stato all'interno del componente di livello superiore.

+0

Ottimo testo, Brandon! Un consiglio che mi piacerebbe dare è quello di clonare sempre i puntelli o le variabili di stato del componente prima di mutarli, come hai fatto nel metodo "removeItem". Ho già avuto problemi quando alcuni componenti figlio stavano usando la stessa variabile. C'è un componente aggiuntivo che può aiutare con l'immutabilità: http://facebook.github.io/react/docs/update.html –

+0

Brandon, mi sono interrogato sulla relazione tra gli aiutanti di stato e contro se stavi usando un oggetto datastore all'interno del framework di flusso. Io tendo ad avere solo il componente di primo livello che ascolta il negozio e ne imposta il proprio stato, che si propaga ai suoi figli tramite oggetti di scena.Non mi preoccupo degli helper delle mutazioni o persino della copia dei dati, poiché i componenti reagiscono aggiornando i negozi tramite i messaggi, piuttosto che toccando i valori dei puntelli. Qualche consiglio? – justingordon

+0

@justingordon Non sono sicuro al 100% di aver capito la tua domanda, ma penso che siamo sulla stessa pagina. So che i commenti SO sono difficili da discutere, quindi sentitevi liberi di [contattarmi] (http://brandontilley.com/contact.html). –