2013-12-16 4 views
20

Sono nuovo di React.js e sto lottando per comprendere alcuni concetti chiave da decidere se dovessimo usare questa libreria per la nostra applicazione. Il mio problema principale è in realtà la gestione dell'aggiornamento nel modello recuperato dal server.Passare i nuovi dati del server ai componenti react.js

Immaginate di avere una pagina che dovrebbe mostrare cinque modelli diversi. L'ho costruito nel modo descritto in questo articolo: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html, quindi ho il componente "root" in cui sono passati tutti i 5 modelli e utilizzando gli oggetti di scena stanno andando verso i componenti che contengono questi modelli. Quindi, ora 2 modelli aggiornati (ottengo questi eventi dal mio codice modello che vivono al di fuori dei componenti di reazione) e ho bisogno di riflettere questo sull'interfaccia utente. Qual è il modo migliore per farlo?

sto pensando seguenti opzioni:

  1. Run renderComponent con nuovi dati, ancora una volta e fare affidamento sulle diff DOM reagiscono tecniche. Ho una preoccupazione per questo, dal momento che ho bisogno di farlo su qualsiasi piccolo cambiamento di dati.
  2. Chiama setState per i componenti che supportano questo modello. In questo modo, i dati non diventano prop, ma lo stato che è (da quello che ho capito) non è una buona pratica. Inoltre, non vedo alcun modo per ottenere riferimento al componente figlio al di fuori del componente root.
  3. Avendo più chiamate di renderingComponent, in questo modo avrò accesso a setProps per qualsiasi componente. Ma poi avrò bisogno di fare qualche lavoro per i modelli (per avere tutti i contenitori disponibili sulla pagina) e uccide tutte le idee di reazione.
  4. Avere un componente radice che include tutti i possibili modelli in applicazione visualizzati agli utenti e chiamare setProps per cambiare i modelli. La mia preoccupazione è che a un certo punto questa componente diventerà piuttosto grande e diventerà "spaghetti" + preoccupazioni dal punto 1.

Grazie in anticipo e spero di essere stato in grado di spiegare chiaramente il mio problema.

+1

Il livello del modello usa eventi? se è così puoi usarli per attivare un rendering. – krs

+0

Ho aggiunto una domanda simile riguardante l'utilizzo dei modelli Backbone con React e come aggiornare le viste quando il modello aggiorna http://stackoverflow.com/questions/20371566/handling-backbone-model-collection-changes-in-react -js –

+1

Hai trovato una soluzione? – copndz

risposta

4

Chiamare nuovamente renderComponent con lo stesso componente ma dati diversi equivale a chiamare component.setProps(). Quindi o mantenere tutti i modelli come stato nel minimo comune denominatore, o semplicemente chiamare setProps/renderComponent di nuovo quando cambia.

+0

Hai un solo componente radice che crei tramite renderComponent? O hai un componente separato che esegue il rendering in contenitori diversi per ogni parte logica della tua applicazione? Grazie per la risposta e cercando di aiutarmi! –

4

Se si passano i dati come oggetti di scena verso il basso al componente figlio, è possibile semplicemente aggiornarlo a un livello superiore e imporrà un rendering a tutti i componenti che utilizzano lo stesso oggetto proprietà. Considerate questo semplice esempio:

var World = React.createClass({ 
    render: function() { 
     return <strong>{this.props.name}</strong>; 
    } 
}); 

var Hello = React.createClass({ 
    clickHandler: function() { 
     this.setProps({ name: 'earth' }); 
    }, 
    render: function() { 
     return (
      <div> 
       Hello <World name={this.props.name} /> 
       <button onClick={this.clickHandler}>Click me</button> 
      </div> 
     ); 
    } 
}); 

Ora, quando l'utente fa clic sul pulsante si modifica la proprietà del componente Hello, ma dal momento che hai superato la stessa proprietà (o dati) oggetto per i bambini, che reagiranno ad essa e aggiorna il suo DOM ombra di conseguenza.

Ecco un violino di ciò che intendo: http://jsfiddle.net/xkCKR/

Se si dispone di un oggetto di dati esterni, si può semplicemente passare al componente superiore. Basta ricordare che questo non significa che v'è un legame a doppio senso:

// simple example of a data model 
var Data = { name: 'world' }; 

var World = React.createClass({ 
    render: function() { 
     return <strong>{this.props.data.name}</strong>; 
    } 
}); 

var Hello = React.createClass({ 
    clickHandler: function() { 
     this.setProps({ 
      data: { name: 'earth' } 
     }); 
    }, 
    render: function() { 
     return (
      <div> 
       Hello <World data={this.props.data} /> 
       <button onClick={this.clickHandler}>Click me</button> 
      </div> 
     ); 
    } 
}); 

React.renderComponent(<Hello data={Data} />, document.body); 

Questo funziona perché reagire usa legame di proprietà a senso unico. Ma se il tuo componente figlio aggiorna le sue proprietà, non salirà al genitore. Per questo è necessario il ReactLink add-on o utilizzare un'interfaccia pub/sub come quella fornita da Backbone.

+3

Tutto quello che stai facendo qui da dentro React, e quello che penso che l'op vuole è "aggiornare" il dom dai reactjs esterni. – copndz

+0

sì, e anche io non capisco il concetto unidirezionale, ecco un bivio di ripresa, una volta fatto, anche se cambio i dati nel modello esterno questo non ha alcun effetto nella classe renderizzata: this.props.model.modelproperty non viene controllato per gli aggiornamenti –

+2

Non è necessario modificare gli oggetti di scena, è necessario riflettere qualsiasi modifica nei dati chiamando setState, solo questo attiverà un rerendering dei componenti e dei sottocomponenti interessati. – markus

4

Al momento so almeno tre modi per passare i nuovi dati a un componente:

  1. componente ri-renderizzare. Non preoccuparti dell'efficienza di questo metodo perché React sembra gestirlo molto bene. Ci sono bei articoli su questo: Change And Its Detection In JavaScript Frameworks e Updating with React.render
  2. Usa PubSub per consentire al componente di essere informato sulla modifica dei dati (alcuni esempi utili che puoi trovare nel post How to communicate between React components).
  3. vincolante con un callback (vedere tre jsfiddles qui sotto)

Per la terza opzione mi sono ispirato la risposta di StevenH ed esteso un po '. Si prega di verificare la mia implementazione a j sfiddle.net/kb3gN/12002/.

var Data = { value: 1 }; 

var dataChange = function(callback){ 
    if(callback){ 
     callback(Data); 
     setInterval(function(){ 
      Data.value++; 
      callback(Data); 
     }, 1000); 
    } 
    return Data; 
}; 

var World = React.createClass({ 
    render: function() { 
     return <strong>{this.props.data.value}</strong>; 
    } 
}); 

var Hello = React.createClass({ 
    getInitialState: function() { 
     return { 
      data: this.props.dataChange() 
     }; 
    }, 
    componentDidMount: function() { 
     this.props.dataChange(this.updateHandler) 
    }, 
    updateHandler: function(data) { 
     this.setState({ 
      data: data 
     }); 
    }, 
    render: function() { 
     return (
      <div> 
       Value: <World data={this.state.data} /> 
      </div> 
     ); 
    } 
}); 

React.renderComponent(<Hello dataChange={dataChange} />, document.body); 

Inoltre esiste una versione estesa a jsfiddle.net/kb3gN/12007.

function ListenersService(){ 
    var listeners = {}; 
    this.addListener = function(callback){ 
     var id; 
     if(typeof callback === 'function'){ 
      id = Math.random().toString(36).slice(2); 
      listeners[id] = callback; 
     } 
     return id; 
    } 
    this.removeListener = function(id){ 
     if(listeners[id]){ 
      delete listeners[id]; 
      return true; 
     } 
     return false; 
    } 
    this.notifyListeners = function(data){ 
     for (var id in listeners) { 
      if(listeners.hasOwnProperty(id)){ 
      listeners[id](data); 
      } 
     } 
    } 
} 

function DataService(ListenersService){ 
    var Data = { value: 1 }; 
    var self = this; 

    var listenersService = new ListenersService(); 
    this.addListener = listenersService.addListener; 
    this.removeListener = listenersService.removeListener; 
    this.getData = function(){ 
     return Data; 
    } 

    setInterval(function(){ 
     Data.value++; 
     listenersService.notifyListeners(Data); 
    }, 1000); 
} 
var dataSevice = new DataService(ListenersService); 

var World = React.createClass({ 
    render: function() { 
     return <strong>{this.props.data.value}</strong>; 
    } 
}); 

var Hello = React.createClass({ 
    getInitialState: function() { 
     return { 
      data: this.props.dataService.getData() 
     }; 
    }, 
    componentDidMount: function() { 
     this.props.dataService.addListener(this.updateHandler) 
    }, 
    updateHandler: function(data) { 
     this.setState({ 
      data: data 
     }); 
    }, 
    render: function() { 
     return (
      <div> 
       Value: <World data={this.state.data} /> 
      </div> 
     ); 
    } 
}); 

React.renderComponent(<Hello dataService={dataSevice} />, document.body); 

Questa implementazione non è completamente seguendo l'idea di componenti isolati (perché componente Ciao dipende dalla API DataService), ma può essere estratta ulteriormente ed è compito dello sviluppatore di app che convenzioni specifiche app suoi componenti seguirà. Per esempio vedi il mix del primo e del secondo esempio su jsfiddle.net/kb3gN/12015 (oggetto HelloDataStatic e callbackHataDataDynamic)

Nota: il servizio Listeners utilizzato nell'esempio segue il pattern Observer e il pattern stesso ha più svantaggi che pro in molti scenari. Oltre a ciò, quello che volevo mostrare con questi esempi è il modo di legare i dati con un callback

<div id="static"></div> 
<div id="dynamic"></div> 
<script> 

function ListenersService(){ 
    var listeners = {}; 
    this.addListener = function(callback){ 
     var id; 
     if(typeof callback === 'function'){ 
      id = Math.random().toString(36).slice(2); 
      listeners[id] = callback; 
     } 
     return id; 
    } 
    this.removeListener = function(id){ 
     if(listeners[id]){ 
      delete listeners[id]; 
      return true; 
     } 
     return false; 
    } 
    this.notifyListeners = function(data){ 
     for (var id in listeners) { 
      if(listeners.hasOwnProperty(id)){ 
      listeners[id](data); 
      } 
     } 
    } 
} 

function DataService(ListenersService){ 
    var Data = { value: 1 }; 
    var self = this; 

    var listenersService = new ListenersService(); 
    this.addListener = listenersService.addListener; 
    this.removeListener = listenersService.removeListener; 
    this.getData = function(){ 
     return Data; 
    } 

    setInterval(function(){ 
     Data.value++; 
     listenersService.notifyListeners(Data); 
    }, 100); 
} 
var dataSevice = new DataService(ListenersService); 
var halloDataDynamic = function(callback){ 
    var data = dataSevice.getData(); 
    if(callback){ 
     dataSevice.addListener(function(data){ 
      callback(data); 
     }); 
    } 
    return data; 
}; 
var halloDataStatic = dataSevice.getData(); 

var World = React.createClass({ 
    render: function() { 
     return <strong>{this.props.data.value}</strong>; 
    } 
}); 

var Hello = React.createClass({ 
    getInitialState: function() { 
     var data; 
     if(typeof this.props.halloData === 'function'){ 
      data = this.props.halloData(this.updateHandler) 
     } 
     else data = this.props.halloData; 
     return { 
      data: data 
     }; 
    }, 
    updateHandler: function(data) { 
     this.setState({ 
      data: data 
     }); 
    }, 
    render: function() { 
     return (
      <div> 
       Value {this.props.name}: <World data={this.state.data} /> 
      </div> 
     ); 
    } 
}); 
</script> 

React.renderComponent(<Hello halloData={halloDataStatic} name="static"/>, document.getElementById('static')); 
React.renderComponent(<Hello halloData={halloDataDynamic} name="dynamic"/>, document.getElementById('dynamic'));