0

I've retrieved a list of categories using an API. Now I want to fetch images from an URL based on the categories. I tried using each category to fetch images from another API, but I'm not sure how to do it.

import React, { useEffect, useState } from 'react';
import './css/Category.css';

function Category() {

    useEffect(() => {
        fetchData();
        getImage();
    }, []);

    const [categories, setCategories] = useState([]);
    const [image, setImage] = useState('');

    const fetchData = async () => {
        const data = await fetch('https://opentdb.com/api_category.php')
        const categories = await data.json();
        console.log(categories.trivia_categories)
        setCategories(categories.trivia_categories)
    }

    const getImage = async (name) => {
        console.log(name)
        const q = name.split(' ').join('+')
        const img = await fetch(`https://pixabay.com/api/?key=apikey&q=${q}&image_type=photo`)
        const image = await img.json();
        console.log(image)
        setImage(image.previewURL)

    }
    return (
        <div className="categories">
            Yesss
            <div className="category-grid">
                {categories.map(category => (
                    <div className="category">
                        {category.name}
                        <img src={getImage(category.name)} /> //do not know what to do here to fetch image of the respective category 
                    </div>
                ))}
            </div>


        </div>
    )
}

export default Category;

After changes suggested by Noah, I was able to show only one image.

const getImage = async (name) => {
        const query = stringMan(name.name)
        console.log(query)
        const img = await fetch(`https://pixabay.com/api/?key=17160673-fd37d255ded620179ba954ce0&q=${query}&image_type=photo`)
        const image = await img.json();
        console.log(image)
        setImage({ [name.name]: image.hits[0].largeImageURL })
    }
return (
        <div className="categories">
            Yesss
            <div className="category-grid">
                {categories.map(category => (
                    <div className="category" key={category.id}>
                        {category.name}
                        <img key={category.id} src={image[category.name]} />
                    </div>
                ))}
            </div>
        </div>
    )

2 Answers 2

1

There are a couple of changes that you can make here.

One issue that I see is that you have a single image variable, that's being re-used for every single category. So when you map over a list of categories (for example let's say we have categories: [history, science, and math]). The current code will call getImage three times, with history, science, and math as parameters.

However, there is only one state variable that is being written to. Which means the last execution of setImage is the only one that will be preserved.

So, you might want to change image from being the URL of a category image, to an object that has the shape:

{
   history: [url],
   science: [url],
   math: [url]
}

The other change to make is that you are calling the getImage() function directly in the rendered output <img src={getImage(category.name)} />. Instead, this should simply use the value that was assigned to the image state: <img src={image} />.

To actually fetch the image, you can use the useEffect hook (https://reactjs.org/docs/hooks-effect.html) to react to changes to the categories variable. That might look something like:

useEffect(() => {
   categories.forEach((c) => getImage(c));
}, [categories]);

The useEffect hook will invoke the function it is given, whenever the dependencies change. This will allow you to trigger the getImage function in response to changes to the categories.

Sign up to request clarification or add additional context in comments.

4 Comments

so if I make the image state, a key-value pair, how would I access the url inside the img tag?
It could look something like: <img src={images[category.name]} /> assuming images is the name of the variable that holds the map of category to image name.
Okay I tried that, but it only displays one image. I don't understand why
I have added the changes in the question, can you check and tell me what is wrong here? Oh and the stringMan() function is suggesting adding + instead a space in the category name, because the API accepts the multi word category like that.
1

There're lot of improvement that could be done as stated by @noah-callaway above/below but coming straight to the point you need to simply fix the URI creation logic to use encodeURIComponent like below:

import React, { useEffect, useState } from 'react';

function Category() {

useEffect(() => {
    fetchData();
    getImage();
}, []);

const [categories, setCategories] = useState([]);
const [image, setImage] = useState('');

const fetchData = async () => {
    const data = await fetch('https://opentdb.com/api_category.php')
    const categories = await data.json();
    console.log(categories.trivia_categories)
    setCategories(categories.trivia_categories)
}

const getImage = async (name) => {
    return encodeURI(`https://pixabay.com/api/?key=apikey&q=${encodeURIComponent(name)}&image_type=photo`)
}
return (
    <div className="categories">
        Yesss
        <div className="category-grid">
            {categories.map(category => (
                <div className="category">
                    {category.name}
                    <img src={getImage(category.name)} />
                </div>
            ))}
        </div>


    </div>
)
}

don't have the api key so can't test but it'll give you something like

https://pixabay.com/api/?key=apikey&q=Entertainment%3A%20Comics&image_type=photo

good luck, hope it works.

1 Comment

But the API wouldn't support that URL

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.