Search code examples
reactjsfirebaseuse-effect

Having trouble using Firebase storage + useEffect hook + useState hook to render images


Have searched up useEffect calling conventions, async and await usage, but for some reason I still cant get my code to work.

I have a list of image names stored in data.recipeImgs, and each name uses the formula of data.id + i + ".pdf", where i is an index from 0 to length of data.recipeImgs. I simply wish to fetch every single image download url from firebase storage, and store those urls in a state variable so later on I can map each url to an img tag. What am I doing wrong? I keep printing out the value of the state variable imgList after each .then, and although I am getting the new image urls, the state variable never changes.

// image slideshow for recipe instructions
const [imgList, setImgList] = React.useState([]);

useEffect(() => {
        // function for firebase storage
        const getImg = async (i) => {
            var storageRef = firebase.storage().ref();

            // Create a reference to the file we want to download
            var imgRef = storageRef.child(data.id + i + ".pdf");

            // Get the download URLs for each image
            await imgRef
                .getDownloadURL()
                .then((url) => {
                    console.log("got one ", imgList);

                    // append new image url to state var
                    setImgList([...imgList, url]);
                })
                .catch((error) => {
                    console.log(error);
                });
        };

        // make sure data exists before trying to fetch all the images
        // from firebase storage
        if (data) {
            for (let i = 0; i < data.recipeImgs.length; i++) {
                getImg(i);
            }
        }
    }, [data]);

    // wait for data to finish loading
    if (!data) {
        return "Loading...";
    }

This is for a personal project, and any help would be appreciated, I'm sure I'm doing something stupid

Edit: added map function relevant code Here is the code for the myList mapping:

                    <ui.Grid container>
                        <ui.Grid item xs={12}>
                            {imgList.map((url, ind) => {
                                <img src={url} alt="Recipe image" key={ind} />;
                            })}
                        </ui.Grid>
                    </ui.Grid>

Solution

  • You should use a functional update (doc)

    setImgList((imgList) => ([...imgList, url]));
    

    What was happening here was that the imgList in the callback's scope was always the initial value [] and you override the state value with the latest url

    await imgRef
        .getDownloadURL()
        .then((url) => {
            // here imgList has always the initial value []
            setImgList([...imgList, url]);
        })