Jerry123
Jerry123

Reputation: 67

Render 'async' function return value in render method (React)

I'm trying to render a simple return value from an async function in my react app however every time I try to my entire app will not render at all. Without calling the function my app renders fine. Also if I console.log my return value (result) in my function it returns the correct value on the console but nothing in the app renders. Any ideas what's going on?

class TitleCards extends Component {
    constructor(props){
        super(props)
        this.totalPortfolio = this.totalPortfolio.bind(this);
        this.getIntradayPrice = this.getIntradayPrice.bind(this);

    }

    async getIntradayPrice(tick) {
        const resp = await fetch(`${IEX.base_url}/stock/${tick}/intraday-prices?chartLast=1&token=${IEX.api_token}`);
        return resp.json();
      }

    async totalPortfolio() {
        const respPromises = this.props.info.map(({ tick }) => this.getIntradayPrice(tick));
        const respArrays = await Promise.all(respPromises);
        console.log(respArrays);
        const result = respArrays.reduce((acc, val, index) => acc + val[0].close * this.props.info[index].amtPurch, 0)
        console.log(result);
        return result;
      }
      
    render(){  
        return(
            <div className="positioning">
                <div className="StockCardTitle">
                    <img src={Folder} className="StockCardTitle-image" /> 
                    {this.totalPortfolio()}
                </div>
            </div>
        )
    }
}

export default TitleCards;

Upvotes: 1

Views: 840

Answers (1)

Drew Reese
Drew Reese

Reputation: 202605

Issue

React components and lifecycle are 100% synchronous, especially the render method. The render method is also considered a pure function, meaning it should have zero side effects (like fetching data!!).

Solution

You should refactor your code to fetch data in one or both of componentDidMount and componentDidUpdate and save the result to local component state for rendering.

Here's an example refactorization.

class TitleCards extends Component {
  constructor(props){
    super(props);

    state = {
      portfolioTotal: '',
    };

    this.totalPortfolio = this.totalPortfolio.bind(this);
    this.getIntradayPrice = this.getIntradayPrice.bind(this);
  }

  async getIntradayPrice(tick) {
    const resp = await fetch(`${IEX.base_url}/stock/${tick}/intraday-prices?chartLast=1&token=${IEX.api_token}`);
    return resp.json();
  }

  async totalPortfolio() {
    const { info } = this.props;
    const respPromises = info.map(({ tick }) => this.getIntradayPrice(tick));
    const respArrays = await Promise.all(respPromises);
    const result = respArrays.reduce((acc, val, index) => acc + val[0].close * info[index].amtPurch, 0)
    return result;
  }

  // When the component mounts, call totalPortfolio
  componentDidMount() {
    this.totalPortfolio()
      .then(portfolioTotal => {
        this.setState({
          portfolioTotal
        });
      })
      .catch(error => {
        // add any required error handling/messaging here
      });
  }

  render() {
    const { portfolioTotal } = this.state;
    return(
      return(
        <div className="positioning">
          <div className="StockCardTitle">
            <img src={Folder} className="StockCardTitle-image" /> 
            {portfolioTotal} // <-- render state value
          </div>
        </div>
    );
  }
}

Upvotes: 1

Related Questions