0

I am trying to do fetches within nested for of loops. The outer for loop writes out an jsx unordered list based on property values found in a passed in obj. If any of the properties contain an array as it's value, it calls a function (buildListFromArray) that writes out an unordered list within the parent unordered list based on the array's values. All elements of the array will contain a url that returns json with only one obj. I want to do a fetch to each url in the array to get a name property value from it so that I can display the name property of the returned obj rather than the raw url values that the property contains.

I would try to use Promises.all() using an array of promises that I would build, but I don't think that's going to make a difference. I could somehow use the property name to go back and insert the jsx with retrieved values because each property name is unique. But I will not know the property names that need a nested unordered list written out until runtime, so that won't work. Below is the code, hopefully well commented

I don't necessarily need code, I'm just looking for ideas on how to approach it. I'm stumped.

//global incrementing counter used rather than array index to set key 
attribute of list item 
//because other arrays will be built and have the same indexes 
//losing uniqueness for list item key values
let counter = 0;

const buildListFromArray = (listArray, clickFunction) => {
    const output = [];
    for (const item of listArray) {
        counter++;

        //array elements can have a url that returns json but I want to replace the raw url
        //that is displayed by fetching results from that url and displaying it's name property
        //so that users see which name or title the url goes to rather than the url itself
        if (item.includes('https://swapi.co/api/')) {
            fetch(item)
            .then((response) => response.json())
            .then((jsonObj) => {output.push(
                <li className='linkStyle' onClick={clickFunction} key={counter}>
             {jsonObj.results[0].name}
            </li>);})
            .catch((error) => {console.log(error)});
        }
        else {
        output.push(<li key={counter}>{item}</li>);
        }
    }   
    return (<ul>{output}</ul>);
};

buildListArray called from outer for in loop (minimal code)

cardJsx.push(<li key={prop}><strong>{prop}:</strong> 
{buildListFromArray(value, props.singleCard)}</li>);
Bobh
  • 321
  • 1
  • 14
  • Generally it is a bad idea to make network calls in a loop. Is there any way you can pre-fetch all of your data then just work with simple arrays in your loop? – Marie Jul 19 '19 at 16:02
  • You can't use asynchronous code to create React elements. – AKX Jul 19 '19 at 16:02
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – hindmost Jul 19 '19 at 16:03
  • I could use Promises.All as a pre-fetch of sorts, but since the for in loop has completed and all line items are written out before results are returned, I do not know which
  • elements to insert the returned values
  • – Bobh Jul 19 '19 at 16:09
  • I can clearly see why you can't use asynch code with react elements, because they will render before results are returned, so how is something like this done? – Bobh Jul 19 '19 at 16:11
  • Thinking on how to do a pre fetch. I suppose that's the only way. – Bobh Jul 19 '19 at 16:16
  • https://reactjs.org/docs/faq-ajax.html – jered Jul 19 '19 at 18:24