2015-06-19 7 views
17

Ich bin neu zu reactjs, ich möchte Daten im Server abrufen, so dass es Seite mit Daten an den Client senden wird.React fetch Daten im Server vor dem Rendern

Es ist OK, wenn die Funktion getDefaultProps Dummy-Daten wie folgt zurückgibt {data: {books: [{..}, {..}]}}.

Allerdings nicht mit Code unten arbeiten. Der Code ausführen in dieser Reihenfolge mit der Fehlermeldung "Can not Eigenschaft 'Bücher' undefinierter lesen"

  1. getDefaultProps
  2. Rückkehr
  3. holen
  4. {Daten: {Bücher: [{..}, { ..}]}}

Allerdings erwarte ich, dass der Code sollte in dieser Reihenfolge laufen

  1. getDefaultProps
  2. holen
  3. {Daten: {Bücher: [{..}, {..}]}}
  4. Rückkehr

Jede Idee?

statics: { 
    fetchData: function(callback) { 
     var me = this; 

     superagent.get('http://localhost:3100/api/books') 
     .accept('json') 
     .end(function(err, res){ 
      if (err) throw err; 

      var data = {data: {books: res.body} } 

      console.log('fetch');     
      callback(data); 
     }); 
    } 


getDefaultProps: function() { 
    console.log('getDefaultProps'); 
    var me = this; 
    me.data = ''; 

    this.fetchData(function(data){ 
     console.log('callback'); 
     console.log(data); 
     me.data = data;  
     }); 

    console.log('return'); 
    return me.data;    
    }, 


    render: function() { 
    console.log('render book-list'); 
    return (
     <div> 
     <ul> 
     { 
      this.props.data.books.map(function(book) { 
      return <li key={book.name}>{book.name}</li> 
      }) 
     } 
     </ul> 
     </div> 
    ); 
    } 

Antwort

3

In Reaktion, props werden für Komponentenparameter verwendet wird, nicht für die Datenverarbeitung. Dafür gibt es ein separates Konstrukt namens state. Immer, wenn Sie state aktualisieren, rendert sich die Komponente grundsätzlich nach den neuen Werten.

var BookList = React.createClass({ 
    // Fetches the book list from the server 
    getBookList: function() { 
    superagent.get('http://localhost:3100/api/books') 
     .accept('json') 
     .end(function(err, res) { 
     if (err) throw err; 

     this.setBookListState(res); 
     }); 
    }, 
    // Custom function we'll use to update the component state 
    setBookListState: function(books) { 
    this.setState({ 
     books: books.data 
    }); 
    }, 
    // React exposes this function to allow you to set the default state 
    // of your component 
    getInitialState: function() { 
    return { 
     books: [] 
    }; 
    }, 
    // React exposes this function, which you can think of as the 
    // constructor of your component. Call for your data here. 
    componentDidMount: function() { 
    this.getBookList(); 
    }, 
    render: function() { 
    var books = this.state.books.map(function(book) { 
     return (
     <li key={book.key}>{book.name}</li> 
    ); 
    }); 

    return (
     <div> 
     <ul> 
      {books} 
     </ul> 
     </div> 
    ); 
    } 
}); 
+0

em ... aber ich weiß, dass die Daten holen und bereit sein können, bevor machen. Ihr Beispiel wird zweimal gerendert. Zuerst rendern (mit leeren Requisiten)> Daten abrufen und setState> erneut mit Status rendern. Korrigieren Sie mich, wenn ich falsch liege – fingercross

+0

Sie haben Recht, die Komponente wird zweimal rendern. Ajax-Aufrufe auszuführen ist asynchron, was das Problem mit dem ursprünglichen Code war. Die einzige Methode, mit der Sie die Daten abrufen, bevor der Rendervorgang ausgeführt wird, besteht darin, dass Sie über eine übergeordnete Komponente verfügen, die für das Abrufen von Daten und das Mounten von 'BookList' zuständig ist. Sogar in diesem Fall müssen Sie den Status der Komponente aktualisieren, die den Rendervorgang zweimal aufruft. – hkal

12

Was Sie suchen, ist componentWillMount.

Vom documentation:

einmal aufgerufen, sowohl auf dem Client und dem Server, unmittelbar vor dem Anfangs Rendering auftritt. Wenn Sie innerhalb dieser Methode setState aufrufen, wird render() den aktualisierten Status anzeigen und nur einmal ausgeführt werden trotz der Statusänderung.

So würden Sie so etwas tun:

componentWillMount : function() { 
    var data = this.getData(); 
    this.setState({data : data}); 
}, 

Auf diese Weise render() wird nur einmal aufgerufen werden, und Sie werden die Daten die Sie suchen haben in der Anfangs machen.

+24

... aber wenn getData eine asynchrone Anfrage auslöst? – nav

+6

@nav Bei asynchronen Anforderungen müssen Sie einen Anfangszustand festlegen, der dem Benutzer anzeigt, dass Daten abgerufen werden (möglicherweise ein Ladesymbol). Sie können den Abruf weiterhin in 'componentWillMount' durchführen. Wenn die Daten abgerufen werden, können Sie diesen Status erneut festlegen, um anzugeben, dass die Daten vollständig geladen sind, und sie dem Benutzer anzeigen. –

+1

Ja, aber dann werden die Daten auf dem Client und nicht auf dem Server abgerufen. Fordert das OP nicht auf, diesen Roundtrip zu speichern, wenn das, was angefordert wird, auf dem Server gerendert wird und die asynchrone Anfrage denselben Server trifft, ist es sicherlich besser, dies alles VOR dem Senden der Antwort an den Client zu verarbeiten? – nav

0

Als Ergänzung der Antwort von Michael Parker, können Sie getData eine Rückruffunktion zu aktivieren akzeptieren die setState Aktualisierung der Daten:

componentWillMount : function() { 
    var data = this.getData(()=>this.setState({data : data})); 
}, 
1

Die beste Antwort, die ich verwende, um Daten von Server zu empfangen und anzeigen es

constructor(props){ 
      super(props); 
      this.state = { 
       items2 : [{}], 
       isLoading: true 
      } 

     } 

componentWillMount(){ 
axios({ 
      method: 'get', 
      responseType: 'json', 
      url: '....', 

     }) 
      .then(response => { 
       self.setState({ 
        items2: response , 
        isLoading: false 
       }); 
       console.log("Asmaa Almadhoun *** : " + self.state.items2); 
      }) 
      .catch(error => { 
       console.log("Error *** : " + error); 
      }); 
    })} 



    render() { 
     return(
     { this.state.isLoading && 
        <i className="fa fa-spinner fa-spin"></i> 

       } 
       { !this.state.isLoading && 
      //external component passing Server data to its classes 
        <TestDynamic items={this.state.items2}/> 
       } 
     ) }