2
votes

I am using a useEffect hook to make an API call depending on data from a photos prop being passed in.

const ImageCarousel = ({ src, photos }) => {
  const [photoList, setPhotos] = useState([]);
  const storageRef = firebase.storage().ref();
  console.log("photos prop:", photos);

  const getImage = photoId => {
    return new Promise((resolve, reject) => {
      storageRef
        .child("images/" + photoId)
        .getDownloadURL()
        .then(url => resolve(url))
        .catch(error => reject(error));
    });
  };

  useEffect(() => {
    console.log("trigger");
    Promise.all(
      photos.map(async photoId => {
        const url = await getImage(photoId);
        return url;
      })
    )
      .then(photoUrls => {
        setPhotos(photoUrls);
      })
      .catch(e => console.log(e));
  }, [photos]);

return (...)
}

I have passed the photos prop into the dependency array of my useEffect hook, so it should fire when the photos prop changes. I can see that the prop is indeed changing in the console, but my useEffect hook does not fire when the prop change comes through. This is what appears in my console:

Console logs

Why is the useEffect not firing twice? From my understanding it should fire when the component renders the first time, and fire again every time the photos prop changes.

3
Did you try to pass different photo prop value to the component and checked whether the useEffect is triggered ?. useEffect will trigger only when the new value is different from the Old Value.Raj Kumar
Yes, have tried using a different prop instead of passing an empty array and it still does not trigger the useEffect.laythq
as per the console screenshot, i felt your passing the same value to the props, thats why i informed like that, if you added your passing props, it will great to check exact issueRaj Kumar

3 Answers

9
votes

Try to make sure your photos prop is immutable, meaning, you send a new array each time there is a change and not mutating the array

1
votes

This may not be the most correct answer, but I've tried this and it works. Hat tip to https://dev.to/stephane/how-to-make-sure-useeffect-catches-array-changes-fm3

useEffect(() => {
  console.log("trigger");
  Promise.all(
    photos.map(async photoId => {
      const url = await getImage(photoId);
      return url;
    })
  )
  .then(photoUrls => {
    setPhotos(photoUrls);
  })
  .catch(e => console.log(e));
}, [JSON.stringify(photos])); // stringify the array used as the trigger, and it'll catch the change
0
votes

useEffect will update if you pass the new data, find the sample code below and check the console.log.

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

    const EffectCheck = ({value}) => {
        console.log('Component Trigger', value);
        useEffect(() => {
            console.log('Updated Effect', value);
        },[value]);

        return (
            <div>{value}</div>
        )
    }

    export default function() {
        const [checkValue, setCheckValue] = useState(Math.random());
        const [dynamicCheckValue, setdyamicCheckValue] = useState(Math.random());

        return (
            <>
                <div><h3>No Update</h3>
                    <EffectCheck value={checkValue}/>
                </div>
                <div><h3>New Update</h3>
                    <EffectCheck value={dynamicCheckValue}/>
                </div>
                <button onClick={() => setCheckValue('Math.random()')}> No Update</button>
                <button onClick={() => setdyamicCheckValue(Math.random())}> New Update</button>
            </>
        );
    }