skip to Main Content

The function getImages is suppose to run once only on page load.

When i refresh the page the both tempQuestionImages and questionImages is empty. But when a hot reload is done it works perfectly.

I use nextJs and firebase,cloud storage if that helps

useEffect(() => {
    async function getImages() {
        await list(storageFolder, { maxResults: 10 }).then((res) => {
            res.items.forEach((item) => {
                tempQuestionImages.push(item.fullPath)
            })
        })
    
        tempQuestionImages.forEach((url) => {
            getDownloadURL(ref(firebaseStorage,url)).then((downUrl) => {
                questionImages[questionImages.length] = downUrl
                setQuestionImages([...questionImages])  
            })
            setLoading(false)
        }) 
    }
    getImages()
}, [])

any kind of help is much appriciated!

Update

So after following the example linked I did this.

useEffect(() => { 
    const storageFolder = ref(firebaseStorage,`/papers/${paper}`)
    async function getImages() {
        setTestList([])
       
        const result = await (await list(storageFolder, { maxResults: 10 })).items.map((item) => { return [...item.fullPath] })
        if (!ignore){
            console.log(result)
            setTestList(result)
        }
    }
       
    let ignore = false
    getImages()

    return () => {
        ignore = true
    }  
}, [])

But still i get the same result. result console logs as an empty array on refresh, but logs as an array with 6 items when hot reload.

5

Answers


  1. Guess you should not keep the !ignore condition logic inside your async function (otherwise, it will be part of the same promise)

    Try the below code:

    useEffect(() => {
        let ignore = false
    
        const storageFolder = ref(firebaseStorage, `/papers/${paper}`)
        const getImages = async () => {
            const result = await (await list(storageFolder, { maxResults: 10 })).items.map((item) => { return [...item.fullPath] })
            return result
        }
        
        getImages().then(data => {
            if (!ignore) {
                setTestList(data)
            }
        })
    
        return () => {
            ignore = true
        }
    
    }, [])
    
    
    Login or Signup to reply.
  2. When you call for the information for tempQuestionImages and questionImages, it might have been processed after you are trying to use tempQuestionImages and questionImages. That’s why it is empty regularly and working during hot reload.

    Login or Signup to reply.
  3. U should add a conditional statement at the start of your page.
    This would make your page wait for data to have the image links before loading your page.
    I had a similar issue and this fixed it.
    Just put an ‘SVG’ with loading animation instead of a static span.

    return(
      <>
        // If 'Data'  does has 'value' then Render Page else 'Loading...'
        {data ? <div>Content</div> : <span>Loading...</span>}
      </>
    )
    

    Or you could just put it on the image component, making it wait by flashing, youtube often does this if you refresh a page, it shows blank page with outlines, only when they have all the data do they show the video.

    Login or Signup to reply.
  4. RCA

    The async function getImages inside useEffect becomes a javascript closure.

    When i refresh the page the both tempQuestionImages and questionImages is empty.

    Hence you get the initial value of tempQuestionImages and questionImages as empty when a component mounts itself for the first time or when the app is reloaded manually..

    But when a hot reload is done it works perfectly

    Whereas, hot reloading allows you to see the changes that you have made in the code without reloading your entire app. Therefore, it doesn’t re-mount the component, instead the component re-renders, henceforth, you have an access to the initial value during the re-render.

    The code part

    Never use, expensive functions in useEffect, because useEffect is mostly to run side-effects, logging, subscribing or pushing events, during the lifecycle of the component.

    If the code is not related to component lifecycle, it should not stay in the useEffect

    Few issues in the useEffect code.
    0. You are using await and then together. Please refer how promises work.

    1. Avoid expensive map. Expensive calculations, should not stay inside useEffect, as it is not related to the component lifecycle here.
    2. Avoid expensive setState in useEffect, setState itself is asynchronous. You can take a look on algorithm on how react update states using queuing mechanism here

    RequirementNow as per your requirement, you need to have this called only once, but not on every re-render.

    So to do this, create a singleton flag pattern in a different file.

    Above code can be wrapped under useMemo. Just don’t need to create any extra tempQuestionImages variable. You can directly, see the below implementation of getImages. Tweak it accordingly, and there you go.

    
    React.useMemo(()=>{
     
        async getImages(){
           const res = await (...whateever)
           res.items.forEach((item)=>{
           const url = item.fullPath
           getDownloadURL(ref(firebaseStorage,url))
           .then((downUrl) => {
                    questionImages[questionImages.length] = downUrl
                    setQuestionImages([...questionImages])  
                })
            })
        }
    },[firebaseStorage, ...addDependencyBasedOnRequirement]);
    
    
    Login or Signup to reply.
  5. Try console.log(storageFolder), I think that paper variable might be empty after first render. Also try to check network tab in dev tools to make sure that your request succeed. And also you might not want to declare functions in useEffect, I don’t think that this is good idea.

    In case if you will find out that your paper variable doesn’t have expected value and as I understand this value wouldn’t change a lot, you can try something like this

    const getImages = async (storageFolder) {
            const response = await list(storageFolder, { maxResults: 10 })
            const result = response.items.map((item) => { return [...item.fullPath]})
    
            console.log(result)
            setTestList(result)
        }
    
    
    useEffect(() => { 
        if (*some condition to check if paper have expected value*) {
            const storageFolder = ref(firebaseStorage,`/papers/${paper}`)
           
            getImages(storageFolder)
        }
    
    }, [paper])
    

    Also I didn’t get what ignore exactly do here, cause it wouldn’t make a request on component dismount, it will just set a variable to true, and after that component mount again, it will became false again, so I removed it.

    I also removed double await from your list request.
    And one more thing, I remove setTestList([]) if you need an array as a default value for this state, just set it in const [testList, setTestList] = useState([]).

    Hope it helps

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search