2015-10-20 19 views
35

Ho un componente che ottiene una raccolta di elementi come oggetti di scena e map s in una raccolta di componenti resi come figli di un componente padre. Utilizziamo le immagini memorizzate in WebSQL come matrici di byte. All'interno della funzione map ottengo un ID immagine dall'elemento e faccio una chiamata asincrona allo DAL per ottenere l'array di byte per l'immagine. Il mio problema è che non posso propagare la promessa in React, dal momento che non è stato progettato per gestire le promesse di rendering (non per quanto posso dire in ogni caso). Vengo da uno sfondo C#, quindi suppongo di cercare qualcosa come la parola chiave await per il codice ramificato resync.Rendering Reagire i componenti con promesse all'interno del metodo di rendering

La funzione map simile a questa (semplificato):

var items = this.props.items.map(function (item) { 
     var imageSrc = Utils.getImageUrlById(item.get('ImageId')); // <-- this contains an async call 
     return (
      <MenuItem text={item.get('ItemTitle')} 
         imageUrl={imageSrc} /> 
     ); 
    }); 

e il metodo getImageUrlById assomiglia a questo:

getImageUrlById(imageId) { 
    return ImageStore.getImageById(imageId).then(function (imageObject) { //<-- getImageById returns a promise 
     var completeUrl = getLocalImageUrl(imageObject.StandardConImage); 
     return completeUrl; 
    }); 
} 

Questo non funziona, ma non so cosa Devo modificare per fare in modo che funzioni. Ho provato ad aggiungere un'altra promessa alla catena, ma poi ricevo un errore perché la mia funzione di rendering restituisce una promessa invece di JSX legale. Stavo pensando che forse ho bisogno di sfruttare uno dei metodi del ciclo di vita React per recuperare i dati, ma poiché ho bisogno che lo props sia già lì, non riesco a capire dove posso farlo.

risposta

59

render() il metodo deve eseguire il rendering dell'interfaccia utente da this.props e this.state, quindi per caricare dati asincroni, è possibile utilizzare this.state per memorizzare la mappatura imageId:imageUrl.
Quindi nel metodo componentDidMount(), è possibile popolare imageUrl da imageId. Quindi il metodo render() dovrebbe essere pura e semplice rendendo l'this.state oggetto

Ecco un esempio di codice rapido:
Si noti che il this.state.imageUrls è popolata in modo asincrono, così la voce di elenco immagine renderizzata appariranno uno dopo l'altro dopo il suo url è inverosimile. È inoltre possibile inizializzare lo this.state.imageUrls con tutti gli ID immagine o indice (senza URL), in questo modo è possibile mostrare un caricatore quando viene caricata l'immagine.

constructor(props) { 
    super(props) 
    this.state = { 
     imageUrls: [] 
    }; 
} 

componentDidMount() { 
    var self = this; 
    this.props.items.map((item) => { 
     ImageStore.getImageById(item.imageId).then(image => { 
      var mapping = {id: item.imageId, url: image.url}; 
      var newUrls = self.state.imageUrls.slice(); 
      newUrls.push(mapping); 

      self.setState({ 
       imageUrls: newUrls 
      }); 
     }) 
    }); 
} 

render() { 
    return (<div> 
     this.state.imageUrls.forEach(mapping => { 
      <div>id: {mapping.id}, url: {mapping.url}`</div> 
     }) 
     </div> 
    ); 
} 
+0

Ok, punto preso. Qualche possibilità di mostrare un esempio? –

+4

Grazie! Questo ha fatto il trucco. L'unica cosa che cambierei qui (almeno per il mio uso) è di farlo in 'componentWillMount' invece di' componentDidMount' per non avere un rendering "vuoto" iniziale. 'componentWillMount' viene eseguito prima del rendering iniziale. –

+1

@Wint C'è un errore su questa riga: 'var newUrls = self.state.imageUrls.slice(). Push (mapping)' perché il valore restituito non è una copia superficiale dell'array ma la lunghezza del nuovo array. Vedi [Array.prototype.push()] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push#Syntax) – jherax

6

O il vostro può semplicemente utilizzare reagire-promessa:

Installare il pacchetto:

npm i react-promise 

e il codice sarà simile modo:

import Async from 'react-promise' 

var items = this.props.items.map(function (item) { 
    var imageSrc = Utils.getImageUrlById(item.get('ImageId')); // <-- this contains an async call 
    return (
     <Async promise={imageSrc} then={(val) => <MenuItem text={item.get('ItemTitle')} imageUrl={val}/>} />  
    ); 
}); 

documentazione completa: https://github.com/capaj/react-promise

Problemi correlati