user14459580
user14459580

Reputation: 276

React Question about promise in a for loop

I am in the process of learning React and making HTTP requests.

Recently Im trying to implement a dropdown for the webpage that Im working on. In my code I had to loop through an array of id, and make a post request for each of the id to extract the metadata. So Im encountering a problem with the dropdown options. The dropdown options are suppose to be the names for the corresponding id.

The array of id is an array of objects that looks like this

[{key: "someidnumber1", count: 5}, {key: "someidnumber2", count: 5}, {key: "someidnumber3", count: 10},....]

So what I did first is to loop through the id array, and make a post request on each of the id as parameter. This is inside my render method.

render() {
  return(
    <SomeOtherComponent>
      { //Do something to fetch the ids
        let promises = [];
        let names = [];
        let options = [];

        ids.map(id => {
          promises.push(
            axios
              .post(TARGET_META_URL, {
                filters: [
                  {
                    field: "id",
                    values: [id.key]
                  }
                ]
              })
              .then(response => {
                 // adding the name from the data into the names array
                 names.push(response.data[0].name);
              })
        });

        Promise.all(promises).then(() => {
          // Wait for the promises to collection all the names
          // and pass into a new array
          options = [...names];
        }

        return (
          <Dropdown 
            options={options}
          />
        );
       }
    </SomeOtherComponent>
  );
}

My dropdown options after opening it is empty. So I did a couple console log and figured out that the options is declared outside the Promise.all so when the render() method is called, the dropdown takes in an empty array. I need help on how to setup the options for the dropdown so it waits for all the code before it finish running. I tried putting the second return inside the Promise.all() but I get an error method saying that render() doesn't have a return.

Upvotes: 1

Views: 161

Answers (2)

Vikrant
Vikrant

Reputation: 898

Maybe this will help -

componentDidMount() {
    let promises = [];
    let options = [];
    ids.map(id => {
        promises.push(
            axios
            .post(TARGET_META_URL, {
            filters: [
                {
                    field: "id",
                    values: [id.key]
                }
            ]
        })
    });

    Promise.all(promises).then((response) => {
    // Wait for the promises to collection all the names
    // and pass into a new array
    options = response.map(res => res.data[0].name);
    this.setState({ options })
    }
}

render() {
    return(
        <SomeOtherComponent>
        { this.state.options?.length ? <Dropdown options={this.state.options} /> : null }
        </SomeOtherComponent>
    );
}

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370679

Make another component which fetches the data and renders them once the responses have come back. Use Promise.all to wait for all of the Promises to resolve together.

const getName = id => axios
    .post(TARGET_META_URL, {
        filters: [
            {
                field: "id",
                values: [id.key]
            }
        ]
    })
    .then(response => response.data[0].name);
const AsyncDropdown = ({ ids }) => {
    const [options, setOptions] = useState();
    useEffect(() => {
        Promise.all(ids.map(getName))
            .then(setOptions)
            .catch((err) => {
                // handle errors
            });
    }, [ids]);
    return options ? <Dropdown options={options} /> : null;
}

And replace your original render method with:

render() {
  return(
    <SomeOtherComponent>
      <AsyncDropdown ids={ids} />
    </SomeOtherComponent>
  );
}

Upvotes: 2

Related Questions