Billwee
Billwee

Reputation: 7

Buttons won't render in React after mapping through array

I'm just learning React. I have two API calls to the backend MongoDB to pull names of vacations related to a user account. The names are saved to a JSON object and I'm trying to map through it to generate buttons on my React side but it's rendering nothing in that div. Everything else renders to the page. API calls are a mess because I thought that was the issue at first.

Profile Page

import React, { Component } from "react";
import Wrapper from "../components/Wrapper";
import { Container, Row, Col } from "../components/Grid";
import PastVacations from "../components/PastVacations";
import VacationBtn from "../components/VacationBtn"
import API from "../utils/API";

class Profile extends Component {
  state = {
    vacaIDs: [],
    vacaNames: []
  }
  componentDidMount() {
    this.getUser()
  }


  getUser = () => {
    let IDsArr = []
    API.getUser("Bill")
      .then((res) => {
        // console.log(res.data) Logs user found
        res.data.vacations.forEach((VacaIDs) => {
          let obj = {}
          obj.name = VacaIDs;
          IDsArr.push(obj)
          // console.log(items) Logs Vacation IDs          
        })
        console.log(IDsArr)
        this.setState({
          vacaIDs: IDsArr
        })
        this.getNames()
      }).catch((err) => {
        console.log(err)
      })
  }

  getNames = () => {
    let namesArr = []
    this.state.vacaIDs.forEach((names) => {
      console.log(names.name)// Logs vacation IDs
      let obj = {}
      API.getVacations(names.name).then((res) => {
        console.log(res.data.name)// Logs Vacation names
        obj.name = res.data.name;
        namesArr.push(obj)
      }).catch((err) => {
        console.log(err.response)
      })
    })
    this.setState({
      vacaNames: namesArr
    })
  }


  render() {
    return (
      <div className="">
        <div className="row justify-content-around">
          <div className="col-md-6">
            {this.state.vacaNames.map(items => (
              <VacationBtn
                name={items.name}
              />
            ))}
          </div>
          <div className="col-md-4">
            <div className="card">
              <h5 className="card-header">
                Card title
                </h5>
              <div className="card-body">
                <p className="card-text">
                  Card content
                    </p>
              </div>
              <div className="card-footer">
                Card footer
                </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default Profile;

VacationBtn Component

import React, { Component } from "react";
import "./style.css";

class VacationBtn extends Component {
  render() {
    return (
      <button type="button" className="btn btn-primary">{this.props.name}</button>
    );
  }
}

export default VacationBtn;

Upvotes: 0

Views: 47

Answers (2)

bekliev
bekliev

Reputation: 2651

As @chandan_kr_jha noticed you're updating state before API is finished it's work.

A bit fancier code below with the same idea behind:

getNames = async () => {
  const promises = this.state.vacaIDs.map((names) => API.getVacations(names.name));
  const vacations = await Promise.all(promises);
  this.setState({
    vacaNames: vacations.map(v => v.data.name),
  });
};

Upvotes: 2

chandan_kr_jha
chandan_kr_jha

Reputation: 557

Use Promise.all Your current code is iterating for API calls but setState happens before any of the api calls are resolved.

getNames = () => {
  let namesArr = [];
  const promises = [];
  this.state.vacaIDs.forEach((names) => {
    promises.push(API.getVacations(names.name));
  });

  Promise.all(promises).then((values) => {
    // do data manipulation here
    values.forEach((val) => {
      namesArr.push(val.data.name);
    });
    this.setState({
      vacaNames: namesArr,
    });
  });
};

Upvotes: 2

Related Questions