andrew196 andrew196 - 1 month ago 23
React JSX Question

ReactJS Promise logic

So I can't figure out this piece of logic regarding a promise in a React component using a js Promise className callback. So what I'm doing is creating a grid item inside a grid and depending on the content of it change the class names "height2" or "width2". So you can see the function "get_image_dimensions" which is called from the "class_name" function when an image is found which is then appended into a string based on the return. But the issue is obviously it's a asynchronous so it will keep running and by the time it gets to the return in the "class_name" function. The reason I can't get around this with a callback is because it's a react component and being called via jsx. If anyone could help that would be great.

If I can clarify anything please let me know.

You can see the code here:

import React from 'react';

class GridItem extends React.Component {
constructor(props) {
super(props);
}

render(){
return (
<div className={this.class_name()}>{this.get_value()}</div>
);
}

class_name(){
let new_class;
let pin = this.props.pin;
let promises = [];

if(pin.type == "text"){
var length = pin.value.length;
if(length > 200) new_class += "width2";
if(length > 500) new_class += "height2";
}else if(pin.type == "image"){
var img_promise = this.get_image_dimensions(pin.value).then(function(img){
let w = img.width,
h = img.height;

if(w>h){
new_class += "width2";
}else if(w<h){
new_class += "height2";
}
})

promises.push(img_promise);
}else if(pin.type == "embed"){
new_class += "width2 height2"
}
return `draggable-section__item draggable-section__item--masonry pin-type-${pin.type}${new_class ? (' '+new_class) : ''}`;
}

get_value(){
let pin = this.props.pin;

if(pin.type == "text"){
return <span>{pin.value}</span>;
}else if(pin.type == "image"){
return <img src={pin.value}/>;
}else if(pin.type == "embed"){
return <iframe src={pin.value}></iframe>;
}
}

get_image_dimensions(url){
return new Promise(function(resolve, reject){
var img = new Image()
img.onload = function(){
resolve(img)
}
img.src = url
})
}
}

export default GridItem;

Answer

It's because you are mixing sync code with async code. To fix it you need to return draggable-section__item draggable-section__item--masonry pin-type-${pin.type}${new_class ? (' '+new_class) : ''}; for every if statement. And return the draggable section only when the promise has been resolved. Also you should check async/await, which is another way to work with promises.

class_name(){
    let new_class;
    let pin = this.props.pin;
    let promises = [];

    if(pin.type == "text"){
        var length = pin.value.length;
        if(length > 200) new_class += "width2";
        if(length > 500) new_class += "height2";
        return `draggable-section__item draggable-section__item--masonry pin-type-${pin.type}${new_class ? (' '+new_class) : ''}`;
    }else if(pin.type == "image"){
        this.get_image_dimensions(pin.value).then(function(img){
            let w = img.width,
                h = img.height;

            if(w>h){
                new_class += "width2";
            }else if(w<h){
                new_class += "height2";
            }
            return `draggable-section__item draggable-section__item--masonry pin-type-${pin.type}${new_class ? (' '+new_class) : ''}`;

        })

    }else if(pin.type == "embed"){
        new_class += "width2 height2"
        return `draggable-section__item draggable-section__item--masonry pin-type-${pin.type}${new_class ? (' '+new_class) : ''}`;
    }        
}