0

I have an API request that uses aysnc and await, grabs the data, then makes a second request with Promise.all, which makes multiple API requests with the id's. That part works out fine.

However, when I go to save the data inside a React hook called, "setItem", it only saves that one and over writes the others. I have a spread operator inside the setItem()

setItems(...items, data)

data being the response from the API request.

My API request is in the top layer of my react app, so I pulled it out into it's own little helper file, that's why "items" and "setItems", are arguments passed through.

    import axios from 'axios';
    import BottleNeck from 'bottleneck'

    const limiter = new BottleNeck({
      maxConcurrent: 1, 
      minTime: 333
    })

    export const Request = (items, setItems) => {

      const getData = () => {
        const options = 'newstories' 
        const API_URL = `https://hacker-news.firebaseio.com/v0/${options}.json?print=pretty`;

        return new Promise((resolve, reject) => {
          return resolve(axios.get(API_URL))
        })
      }

      const getIdFromData = (dataId) => {
        const API_URL = `https://hacker-news.firebaseio.com/v0/item/${dataId}.json?print=pretty`;
        return new Promise((resolve, reject) => {
          return resolve(axios.get(API_URL))
        })
      }

      const runAsyncFunctions = async () => {
        const {data} = await getData()

        Promise.all(
          data.map(async (d) => {
            const {data} = await limiter.schedule(() => getIdFromData(d))
           //****************** issue here ************************//
            setItems([...items, data]);
          })
        )
      }
      runAsyncFunctions()

    }

just in case you want to see the app.js file for reference

    import React, { useState, useEffect } from 'react';
    import './App.css';
    import { SearchBar } from './search-bar';
    import { Results } from './results';
    import { Request } from './helper/request'

    function App() {
      const [input, setInput] = useState('');
      const [items, setItems] = useState([]);

      const handleChange = val => {
        setInput(val)
      }
      // console.log(input)
      // console.log(results)

      // API calls
      // call useEffect here, calls Request(), put results in useEffect

      useEffect(() => {
        Request(items, setItems)
      }, [])

      return (
        <div className="App">
          <SearchBar handleChange={handleChange}/>
          <Results items={items} />
        </div>
      );
    }

    export default App;
2
  • Not sure if I am missing something, but I don't see results actually defined anywhere or used in your helper file. Can you confirm if ...results ever evaluates to something with console log or similar? Commented Feb 19, 2021 at 23:56
  • im sorry, let me edit that Commented Feb 20, 2021 at 0:25

1 Answer 1

2

At your Promise.all return each data, after you can chain with then that passes an array with all resolved data. This way you only need to call it once setItems:

Promise.all(
  data.map(async (d) => {
    const { data } = await limiter.schedule(() => getIdFromData(d));
    return data;
  })
).then((dataResults) => setItems((results) => [...results, ...dataResults]));
Sign up to request clarification or add additional context in comments.

5 Comments

thank you that was helpful, however after implementing the '.then' method and what you describe, when i console.log(results) in the app component to get state, it now just shows an empty array. as if nothing is loading
yea youre right and i realize that, i edited it in the description (because of another persons comment, to make things clearer). but it still does not show anything in state...
i think the problem is, it's waiting to load the whole array of API calls. which is 500. sooo it'd be awhile before i see it in state. would there be a way where I can stop it after 5 or 10 calls, thus 5 or 10 objects in the array?
yup, confirmed it. it showed in state after the Promise.all completed all 500 calls. I guess the next question would be how to split it up into arrays of ten, or more so, do I split it up before the Promise.all fires off
It would require a more refined approach, some sort of pagination where you can break the requests as needed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.