Using fetch in forEach to preload images

Hi. I’m working on my quote machine project. I am having an issue with my preload Images method. Here’s the code:

componentDidMount() {
		console.log("did mount");
		fetch(url)
		.then(function(response) {
			if(!response.ok) {
				throw Error(response.statusText);
			}
			return response.json();
		})
		.then(function(responseAsJson) {
			quotes = responseAsJson;
			console.log("quotes test: " + quotes[45].quote);
		})
		.catch(error => console.log("fetch error: " + error));
		
		this.preloadImages();
	}

	getRandomIndex() {
		return Math.floor(Math.random() * (indexArr.length - 1));
	}	
	
	preloadImages() {	
		console.log("preload got called");
		let x;
		quotes.forEach(function(x) {
			// fetch image
			fetch(quotes.image)
			.then(function(response) {
				if(!response.ok) {
					throw Error(response.statusText);
				}
			return response.blob()
			})
			.then(function(image){
				imageArr.push(image)
			})
			.catch(error => console.log("image fetch error: " + error));
		})
	}	Preformatted text	

I am calling preloadImages() at the end of componentDidMount() but I suspect that the preload is running before json fetch is complete. My console shows:

“did mount”

“preload got called”

[object Error] {}

“quotes test: I always wanted to be somebody, but now I realize I should have been more specific.”

which indicates (i think) that preloadImages is running too early. Any thoughts? Thanks!

Codepen link: [https://codepen.io/CodeMoo/pen/RvQKjX?editors=0001]

You are correct in your suspicion, fetch is an asynchronous action, in preloadImages() you are trying to loop through this async action, but the loop does not wait till the async action has been fulfilled. There is a way to loop with async actions using async/await, but you really should not need this for this project. In componentDidMount(), you are already doing a fetch, I would use that fetch action to set the state with the quotes array, then you can leverage react’s state management to access the quotes any time or any place in your application. You only really need one call to the api as the endpoint always gives the same results, i.e. the same array of quotes.

Thanks for your reply. I should have mentioned, doh! I created preloadImages() because when i click the new quote button, the new quote and author were shown almost immediately but the author’s image took a few more seconds. Seemed unprofessional/wrong. :frowning: I figured preloading the images would solve that problem.

Edit: I have completed the project and turned it in. I am trying to make it portfolio ready now.

sorry, I see in now in your app that they’re fetching different endpoints ? ok, then if you MUST loop thru the fetch, then take a look at async/await , which would look something like…

preloadImages = async () =>{
for (quote of quotes) {
     const response = await fetch('...')
     const image = await response.blob()
     imageArr.push(image)
}
}

but I’d personally try different options as well since I don’t really like looping thru async actions, how about loading the images right after the quotes inside your CDM’s fetch ?

I’m thinking about a fetch within a fetch. I’m going to play around with it and also read up on async. Thank you!