2015-07-30 8 views
31

Ho un problema con uno dei miei componenti React. Penso che AJAX non carichi tutto il contenuto da un server esterno prima che React esegua il rendering del componente ChildComp.Come attendere la risposta AJAX e solo dopo aver eseguito il rendering del componente?

Obj tree

sopra potete vedere l'albero della risposta che è venuta dal server. E questo è il codice del mio componente:

var ChildComp = React.createClass({ 
    getInitialState: function(){ 
    return { 
     response: [{}] 
    } 
    }, 
    componentWillReceiveProps: function(nextProps){ 
    var self = this; 

    $.ajax({ 
     type: 'GET', 
     url: '/subscription', 
     data: { 
     subscriptionId: nextProps.subscriptionId //1 
     }, 
     success: function(data){ 
     self.setState({ 
      response: data 
     }); 
     console.log(data); 
     } 
    }); 
    }, 
    render: function(){ 
     var listSubscriptions = this.state.response.map(function(index){ 
     return (
      index.id 
     ) 
     }); 
     return (
     <div> 
      {listSubscriptions} 
     </div> 
    ) 
    } 
}); 

Questo sta lavorando bene, ma se cambio il mio ritorno a:

return (
    index.id + " " + index.order.id 
) 

id non è definito. E tutte le altre proprietà dell'oggetto ordine. Perché è così? Se I console.log la mia risposta dopo la funzione success fornisce tutti i dati (come nell'immagine). La mia ipotesi è che solo i primi oggetti vengono caricati quando React esegue il rendering del componente e successivamente vengono caricati tutti gli altri oggetti interni. Non posso davvero dire se è così (sembra così strano) né posso dire come risolvere questo problema.

ho provato anche qualcosa di simile

success: function(data){ 
    self.setState({ 
     response: data 
    }, function(){ 
     self.forceUpdate(); 
    }) 
    }.bind(this) 

ma ancora ri-renderizzare accade prima del mio console.log(data) viene attivato. Come aspettare la risposta AJAX e solo dopo aver eseguito il rendering del componente?

+0

Per garantire che i dati di risposta siano formattati come si desidera, è necessario aggiungere 'dataType:" json "' alla richiesta ajax. Inoltre, puoi provare a registrare il nuovo stato appena ricevuto in 'shouldComponentUpdate'. E infine, verifica quali - se ce ne sono - le proprietà si trovano su 'index.order'. Forse contiene una stringa invece di proprietà. –

risposta

25

Ecco il codice rielaborato con alcuni commenti delle parti modificate

getInitialState: function(){ 
    return { 
     // [{}] is weird, either use undefined (or [] but undefined is better). 
     // If you use [], you loose the information of a "pending" request, as 
     // you won't be able to make a distinction between a pending request, 
     // and a response that returns an empty array 
     response: undefined 
    } 
    }, 

loadSubscriptionData: function(subscriptionId){ 
    var self = this; 

    // You probably want to redo the ajax request only when the 
    // subscriptionId has changed (I guess?) 
    var subscriptionIdHasChanged = 
     (this.props.subscriptionId !== subscriptionId) 

    if (subscriptionIdHasChanged) { 
     // Not required, but can be useful if you want to provide the user 
     // immediate feedback and erase the existing content before 
     // the new response has been loaded 
     this.setState({response: undefined}); 

     $.ajax({ 
      type: 'GET', 
      url: '/subscription', 
      data: { 
      subscriptionId: subscriptionId //1 
      }, 
      success: function(data){ 

      // Prevent concurrency issues if subscriptionId changes often: 
      // you are only interested in the results of the last ajax  
      // request that was made. 
      if (self.props.subscriptionId === subscriptionId) { 
       self.setState({ 
        response: data 
       }); 
      } 
      } 
     }); 
    } 
    }, 

// You want to load subscriptions not only when the component update but also when it gets mounted. 
    componentDidMount: function(){ 
    this.loadSubscriptionData(this.props.subscriptionId); 
    }, 
    componentWillReceiveProps: function(nextProps){ 
    this.loadSubscriptionData(nextProps.subscriptionId); 
    }, 

render: function(){ 

     // Handle case where the response is not here yet 
     if (!this.state.response) { 
     // Note that you can return false it you want nothing to be put in the dom 
     // This is also your chance to render a spinner or something... 
     return <div>The responsive it not here yet!</div> 
     } 

     // Gives you the opportunity to handle the case where the ajax request 
     // completed but the result array is empty 
     if (this.state.response.length === 0) { 
      return <div>No result found for this subscription</div>; 
     } 


     // Normal case   
     var listSubscriptions = this.state.response.map(function(index){ 
     return (
      index.id 
     ) 
     }); 
     return (
     <div> 
      {listSubscriptions} 
     </div> 
    ) 
    } 
+0

Non sono nemmeno sicuro di quale sia stato il codice di risoluzione qui, ma suppongo che fosse la funzione 'componentWillMount'. AJAX ora sta caricando tutto il contenuto dal server. Non ha molto senso per me, ma lo esaminerò di più anche se adesso tutto funziona. –

+0

@TomJoad 'componentWillReceiveProps' è, come documentato, non chiamato sul rendering iniziale, quindi è importante utilizzare anche' componentWillMount' –

+0

imposta la proprietà di stato a un errore non definito causato da me. Ma grazie per il suggerimento ho finito usando la proprietà flag 'loaded: false' –

2

Prima di tutto, si desidera mettere la chiamata AJAX all'interno di componentWillMount() o componentDidMount() (se è necessario eseguire alcune manipolazioni DOM).

componentWillReceiveProps() è

Richiamato quando un componente sta ricevendo nuovi puntelli. Questo metodo non viene chiamato per il rendering iniziale.

https://facebook.github.io/react/docs/component-specs.html

Ma anche se si mette la chiamata AJAX all'interno componentWillMount() o componentDidMount(), render() sarà probabilmente chiamato con una risposta vuota prima di finiture AJAX chiamata.

Così l'ordine di chiamata sarebbe come questo (ho assunto si mette la chiamata AJAX all'interno componentWillMount():

componentWillMount() (chiamata AJAX viene attivato) ->componentDidMount() ->render() (rendering con risposta vuota, così index.order.id sarà causa un errore) -> La chiamata AJAX restituisce e chiama this.setState() -> (vengono chiamati alcuni altri metodi del ciclo di vita del componente come shouldComponentUpdate(), vedere il collegamento FB sopra per i dettagli) ->render() viene richiamato.

Quindi la soluzione al vostro problema è:

1) Spostare la chiamata AJAX a uno componentWillMount() o componentDidMount()

2) In entrambi i impostare lo stato iniziale a poco [] (al contrario di [{}]) o controllare se index è vuota in

var listSubscriptions = this.state.response.map(function(index){ 
    return (
    index.id 
) 
}); 
+1

OP utilizza i puntelli ricevuti nei suoi dati di richiesta ajax. Questa risposta non funziona per lui. –

+1

Spostare Ajax in mount 'will' o' did' non risolverà il problema; ci sarà ancora quasi certamente un rendering prima che la chiamata Ajax completi. –

0

ho gettato in un JSBin e io non riesco a riprodurre il problema.

http://jsbin.com/jefufe/edit?js,output

Ogni volta che il pulsante viene premuto, invia nuovi puntelli per <ChildComp/>, che poi innesca la sua componentWillReceiveProps, invia il (finto) richiesta AJAX, riceve il (finto) di risposta, imposta lo stato risposta alla (falso) risposta e re-rende il componente con i dati corretti mostrati.

L'unica cosa che ho aggiunto era un assegno per index.id == null prima di provare ad accedervi, ma non credo che faccia nulla per risolvere il problema che si sta avendo riguardo l'impossibilità di accedere a index.order.id.

L'unico motivo per cui riesco a capire perché non è possibile accedere a index.order.id è perché forse si dispone di un codice che non ci viene mostrato che modifica la risposta a un certo punto.

Inoltre, ricorda che i componenti di React saranno sempre ri-renderizzati quando il loro stato o oggetti di scena sono cambiati, a meno che tu non abbia definito un comportamento in shouldComponentUpdate che dice altrimenti. Ciò significa che quando si riceve l'intero oggetto dal server e lo si imposta su uno stato nel componente, verrà nuovamente visualizzato e visualizzato lo stato aggiornato. Non c'è ragione/ragione per React per "rendere superficialmente" il tuo oggetto di risposta come suggerivi nel tuo post originale.

Non sono sicuro che questa risposta sia d'aiuto, ma spero che tu possa guardare al JSBin che ho fornito e trovare qualcosa che potresti aver trascurato.

Problemi correlati