Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering React components with promises inside the render method

I have a component which gets a collection of items as props and maps them to a collection of components which are rendered as children of a parent component. We use images stored in WebSQL as byte arrays. Within the map function I get an image Id from the item and make an async call to the DAL in order to get the byte array for the image. My problem is that I cannot propagate the promise in to React, since it was not designed to deal with promises in rendering (not as far as I can tell anyway). I come from a C# background, so I guess I'm looking for something like the await keyword for resync-ing branched code.

The map function looks something like this (simplified):

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

and the getImageUrlById method looks like this:

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

This doesn't work, but I don't know what I need to modify to make this work. I tried adding another promise to the chain, but then I get an error because my render function return a promise instead of legal JSX. I was thinking that maybe I need to leverage one of the React life-cycle methods to fetch the data, but since I need the props to already be there, I can't figure out where I can do this.

like image 899
Elad Lachmi Avatar asked Oct 20 '15 16:10

Elad Lachmi


People also ask

How do you render promises in React?

To render React components with promises inside, we can use the usePromise hook provided by the react-promise package. We call usePromise with a promise that resolves to 'hello world' . It returns an object with the value and loading properties. value is the resolved value of the promise.

How do you get promise results from promise object in React?

To get promise value in React and JavaScript, we can use await . to create the getAnswer function that calls fetch with await to get the response data from the promise returned by fetch . Likewise, we do the same with the json method. And then we call setAns to set the value of ans .

What is actually rendered by React render method?

React renders HTML to the web page by using a function called render(). The purpose of the function is to display the specified HTML code inside the specified HTML element. In the render() method, we can read props and state and return our JSX code to the root component of our app.

How are React components rendered?

A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic. Libraries that use render props include React Router, Downshift and Formik. In this document, we'll discuss why render props are useful, and how to write your own.


1 Answers

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>     ); } 
like image 73
Wint Avatar answered Oct 17 '22 02:10

Wint