2013-12-16 8 views
20

Ich bin neu bei React.js und habe Mühe, einige Kernkonzepte zu verstehen, um zu entscheiden, ob wir diese Bibliothek für unsere Anwendung verwenden sollen. Mein Hauptproblem ist die Aktualisierung in einem Modell, das vom Server abgerufen wird.Neue Serverdaten an react.js-Komponenten übergeben

Stellen Sie sich vor, dass ich eine Seite habe, die fünf verschiedene Modelle anzeigen soll. Ich habe es in der in diesem Artikel beschriebenen Weise gebaut: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html, also habe ich "root" -Komponente, wo alle 5 Modelle bestanden und Requisiten verwenden, die sie zu den Komponenten hinuntergehen, die diese Modelle halten. Also, jetzt 2 Modelle aktualisiert (ich erhalte diese Ereignisse von meinem Modell-Code, die außerhalb der reagierenden Komponenten leben) und ich muss dies auf der Benutzeroberfläche widerspiegeln. Was ist der beste Weg, dies zu tun?

Ich denke über folgende Möglichkeiten:

  1. Run renderComponent mit neuen Daten erneut und setze auf dem DOM diff reagieren Techniken. Ich habe damit ein Problem, da ich dies bei jeder kleinen Änderung der Daten tun muss.
  2. Call setState für die Komponenten, die diese Modelle halten. Auf diese Weise werden Daten nicht prop, sondern ein Zustand, der (von dem, was ich verstehe) keine gute Praxis ist. Außerdem sehe ich keine Möglichkeit, die Kind-Komponente außerhalb der Root-Komponente zu referenzieren.
  3. Mit mehreren renderComponent-Aufrufe, so auf diese Weise habe ich Zugriff auf setProps für alle diese Komponente. Aber dann muss ich etwas Templating-Arbeit machen (um alle Container auf der Seite verfügbar zu haben) und es tötet die ganze Reaktionsidee.
  4. Mit einer Wurzel-Komponente, die alle möglichen Modelle in der Anwendung für die Benutzer angezeigt und rufen SetProps zum Ändern von Modellen. Mein Anliegen hier, dass diese Komponente ziemlich groß wachsen und sich als „Spaghetti“ irgendwann + Bedenken von Punkt 1.

Vielen Dank im Voraus und hoffe ich in der Lage war eindeutig zu erklären, mein Problem.

+1

Verwendet Ihre Modellschicht Ereignisse? Wenn ja, können Sie sie zum Auslösen eines Rendervorgangs verwenden. – krs

+0

Ich habe eine ähnliche Frage bezüglich der Verwendung von Backbone-Modellen mit React hinzugefügt und wie Sie die Ansichten aktualisieren sollten, wenn das Modell http://stackoverflow.com/questions/20371566/handling-backbone-model-collection-changes-in-react aktualisiert -js –

+1

Haben Sie eine Lösung gefunden? – copndz

Antwort

4

Der erneute Aufruf von renderComponent mit derselben Komponente, aber anderen Daten entspricht dem Aufruf von component.setProps(). Entweder behalten Sie alle Modelle als Zustand im kleinsten gemeinsamen Nenner oder rufen setProps/renderComponent erneut auf, wenn sich diese ändert.

+0

Haben Sie nur eine Wurzelkomponente, die Sie mit renderComponent erstellen? Oder haben Sie eine separate Komponente, die für jeden logischen Teil Ihrer Anwendung in unterschiedliche Container rendert? Danke für die Antwort und versuchen, mir zu helfen! –

4

Wenn Sie die Daten als Requisiten an Ihre untergeordnete Komponente übergeben, können Sie sie einfach auf einer höheren Ebene aktualisieren und es erzwingen, dass alle Komponenten, die dasselbe Eigenschaftenobjekt verwenden, gerendert werden. Betrachten Sie dieses einfache Beispiel:

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

Nun, wenn der Benutzer auf die Schaltfläche klickt Sie die Eigenschaft auf der Hello Komponente ändern, aber da Sie die gleiche Eigenschaft übergeben (oder Daten) Objekt an die Kinder, werden sie darauf reagieren und aktualisiere sein Schatten-DOM entsprechend. Hier

ist eine Geige von dem, was ich meine: http://jsfiddle.net/xkCKR/

Wenn Sie ein externes Datenobjekt haben, können Sie diese einfach an die Spitze Komponente zu übergeben. Denken Sie daran, dass dies nicht bedeutet, dass es eine Zwei-Wege-Bindung:

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

Dies funktioniert, weil reagieren verwendet Einbahn von Bindungseigenschaften. Aber wenn Ihre Child-Komponente ihre Eigenschaften aktualisieren würde, würde sie nicht zum übergeordneten Objekt hochklettern. Dafür benötigen Sie die ReactLink add-on oder verwenden Sie eine Pub/Sub-Schnittstelle wie die Backbone bietet.

+3

Alles, was Sie hier tun, ist von innen React, und was ich denke, dass das OP will, ist das "refresh" dom von außen reactjs. – copndz

+0

Ja, und ich verstehe auch nicht das One-Way-Konzept, hier ist eine Shoot-Bindung, einmal gemacht, auch wenn ich die Daten im äußeren Modell ändern, hat dies keinen Effekt in der gerenderten Klasse: this.props.model.modelproperty wird nicht auf Updates überprüft –

+2

Sie dürfen Requisiten nicht ändern, Sie müssen jede Änderung in Daten durch Aufrufen von setState widerspiegeln, nur das wird eine Neuausgabe der betroffenen Komponenten und Unterkomponenten auslösen. – markus

4

Im Moment weiß ich zumindest drei Möglichkeiten, um neue Daten zu einer Komponente zu übergeben:

  1. Re-Render-Komponente. Mach dir keine Sorgen über die Effizienz dieser Methode, denn React scheint das sehr gut zu handhaben. Es gibt schöne Artikel darüber: Change And Its Detection In JavaScript Frameworks und Updating with React.render
  2. Verwenden Sie PubSub, damit die Komponente bei Datenänderungen benachrichtigt werden kann (einige hilfreiche Beispiele finden Sie in der How to communicate between React components Post).
  3. mit einem Rückruf Bindung (siehe drei jsfiddles unten)

Für die dritte Option, die ich durch die Antwort von StevenH inspiriert und erweitert es ein wenig. Bitte überprüfen Sie meine Implementierung unter 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); 

Auch gibt es eine erweiterte Version bei 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); 

Diese Implementierung folgt nicht vollständig die Idee der isolierten Komponenten (weil Hallo-Komponente auf dem Data API abhängig ist), aber es kann weiter und ist bis zu dem App-Entwickler die App-Konventionen seiner Komponenten abstrahiert werden wird folgen. Zum Beispiel die Mischung der ersten und zweiten Beispiele unter jsfiddle.net/kb3gN/12015 (halloDataStatic-Objekt und halloDataDynamic-Rückruf)

Hinweis: Der im Beispiel verwendete ListenersService folgt Observer Pattern und das Muster selbst hat in vielen Szenarien mehr Nachteile als Profis. Aber abgesehen davon, was ich mit diesen Beispielen zeigen wollte, ist, dass es eine Möglichkeit der Datenbindung mit einem Rückruf gibt