Rendering React components with promises inside the render method
Solution 1:
render()
method should render UI from this.props
and this.state
, so to asynchronously load data, you can use this.state
to store imageId: imageUrl
mapping.
Then in your componentDidMount()
method, you can populate imageUrl
from imageId
. Then the render()
method should be pure and simple by rendering the this.state
object
Note that the this.state.imageUrls
is populated asynchronously, so the rendered image list item will appear one by one after its url is fetched. You can also initialize the this.state.imageUrls
with all image id or index (without urls), this way you can show a loader when that image is being loaded.
constructor(props) {
super(props)
this.state = {
imageUrls: []
};
}
componentDidMount() {
this.props.items.map((item) => {
ImageStore.getImageById(item.imageId).then(image => {
const mapping = {id: item.imageId, url: image.url};
const newUrls = this.state.imageUrls.slice();
newUrls.push(mapping);
this.setState({ imageUrls: newUrls });
})
});
}
render() {
return (
<div>
{this.state.imageUrls.map(mapping => (
<div>id: {mapping.id}, url: {mapping.url}</div>
))}
</div>
);
}
Solution 2:
Or you can use react-promise :
Install the package :
npm i react-promise
And your code will look something like so :
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}/>} />
);
});
Edit: Oct 2019
The last build of react-promise provide a hook called usePromise
:
import usePromise from 'react-promise';
const ExampleWithAsync = (props) => {
const {value, loading} = usePromise<string>(prom)
if (loading) return null
return <div>{value}</div>}
}
Full docs: react-promise