Search code examples
reactjspromisees6-promise

How to get the promise value in reactjs?


I am trying to render multiple images, stored on firebase, on a single page.

This is the function to get the image url:

async getTokenImage(fileName) {
    const ref = firebase.storage().ref('token/' + fileName);
    const url = await ref.getDownloadURL();
    console.log('in here')
    return url
}

This is the function call:

<div
     class="card-img-top" 
     style={{
        backgroundColor: '#'+token.color, 
        backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
     }}></div>

console logging the "value" gives the needed image url
enter image description here

but returning the "value" gives nothing.
for the sake of testing, I have done this:

let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)

which results in:
enter image description here

So how do I get the image url within the initial function call?

Part of grandfather component:

<MintedTokens
    account={this.state.account} 
    contract={this.state.contract} 
    tokens={this.state.tokens} />

Father component:

import React from 'react';
import "./minted-tokens.scoped.css";

import MultipleMintedTokens from './multiple-minted-tokens.js';
import SingleMintedTokens from './single-minted-token.js';

class MintedTokens extends React.Component {

render() {
    const color = window.location.pathname.split('/')[2]
    let display
    if(color == undefined) {
        display = <MultipleMintedTokens 
                    account={this.props.account}    
                    tokens={this.props.tokens}></MultipleMintedTokens>
    } else {
        display = <SingleMintedTokens
                    account={this.props.account} 
                    color={color} 
                    tokens={this.props.tokens}></SingleMintedTokens>
    }
    return (
        <div class="Minted-Token">
            {display}
        </div>
    );
   }
 }
 export default MintedTokens;

Child component (where the multiple images need to be rendered):

import React from 'react';
import ether from '../../assets/ether.svg'
import firebase from "../../firebase.js"
import { Link } from 'react-router-dom';

class MultipleMintedTokens extends React.Component {

async getTokenImage(fileName) {
    const ref = firebase.storage().ref('token/' + fileName);
    const url = await ref.getDownloadURL();
    console.log('in here')
    return url
}

render() {
    return (
        <div class="welcome minted-tokens">
            <h1 class="">Minted Tokens</h1>
            <div class="row">
                {this.props.tokens.map((token, key) => {
                    let name = '',
                        description = ''
                    if(token.to == this.props.account) {
                        token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
                        token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description

                        let image = this.getTokenImage(token.fileName).then((result) => {return result})
                        console.log(image)
                        
                        return (
                            <Link to={token.tokenURI} key={key}>
                                <div class='token-id'>{token.id}</div>
                                <div class="card card-width">
                                    <div
                                        class="card-img-top" 
                                        style={{
                                            backgroundColor: '#'+token.color, 
                                            backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
                                        }}></div>
                                    <h5 class="card-header" style={{
                                        backgroundColor: '#'+token.color,
                                        color: '#'+token.fontColor}}>{name}</h5>
                                    <div class="card-body">
                                        <p class="card-text">{description}</p>
                                        <div class="foot-of-card">
                                            <span class="row price-row">
                                                <img src={ether} alt="ether" class="icon" />
                                                <p class="card-text">{token.price}</p>
                                            </span>
                                            <p class="card-text datetime">{token.dateTime}</p>
                                        </div>
                                    </div>
                                </div>
                            </Link>
                        )
                    }
                })}
            </div>
        </div>
    );
  }
}
export default MultipleMintedTokens;

Solution

  • I could not test it well but you can try the following:

    Add:

    constructor(props) {
        super(props);
        this.state = {
            urlImgs: [],
          };
        }
    ...
      componentDidMount() {
        const { tokens } = this.props;
        const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
        Promise.all(promiseArray)
          .then(valueArray => {
            this.setState(prevState => ({
              ...prevState,
              urlImgs: valueArray
            }))
          })
          .catch(err => console.log(err));
      }
    ...
    backgroundImage: `url(${urlImgs[key] ?? null})`
    

    All Code:

    class MultipleMintedTokens extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
            urlImgs: [],
          };
        }
    
      async getTokenImage(fileName) {
          const ref = firebase.storage().ref('token/' + fileName);
          const url = await ref.getDownloadURL();
          console.log('in here')
          return url
      }
    
      componentDidMount() {
        const { tokens } = this.props;
        const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
        Promise.all(promiseArray)
          .then(valueArray => {
            this.setState(prevState => ({
              ...prevState,
              urlImgs: valueArray
            }))
          })
          .catch(err => console.log(err));
      }
    
      render() {
        const { urlImgs } = this.state;
        return (
          <div class="welcome minted-tokens">
            <h1 class="">Minted Tokens</h1>
            <div class="row">
              {this.props.tokens.map((token, key) => {
                let name = '',
                description = ''
                if(token.to == this.props.account) {
                  token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
                  token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
                  let image = this.getTokenImage(token.fileName).then((result) => {return result})
                  console.log(image)
                  return (
                    <Link to={token.tokenURI} key={key}>
                      <div class='token-id'>{token.id}</div>
                      <div class="card card-width">
                        <div
                            class="card-img-top"
                            style={{
                                backgroundColor: '#'+token.color,
                                backgroundImage: `url(${urlImgs[key] ?? null})`
                            }}></div>
                        <h5 class="card-header" style={{
                            backgroundColor: '#'+token.color,
                            color: '#'+token.fontColor}}>{name}</h5>
                        <div class="card-body">
                            <p class="card-text">{description}</p>
                            <div class="foot-of-card">
                                <span class="row price-row">
                                    <img src={ether} alt="ether" class="icon" />
                                    <p class="card-text">{token.price}</p>
                                </span>
                                <p class="card-text datetime">{token.dateTime}</p>
                            </div>
                        </div>
                      </div>
                    </Link>
                  )
                }
              })}
            </div>
          </div>
          );
        }
      }
      export default MultipleMintedTokens;
    

    if you have React with version > 16.8, I advice you to start using Stateless Components and Hooks. And read about the lifecycle of React and the render method here: