Home > Back-end >  React - I can't render the correct amount of items in from the map function
React - I can't render the correct amount of items in from the map function

Time:02-06

Image of the unwanted behaviourI'm using DaisyUi / Tailwindcss to render a Carousel and it's child item. The item are images that are fetched from a firebase storage.

The issue is when I try to map the image urls, which is a state of an array of strings, I only get 1 image instead of all the items.

There's 2 items in my storage and it's complete when I log it's content / arrays' length but the state(imageUrls) is not filled and it only shows 1 item.

Ask for any clarification. Here is my code for my carousel component.

const MyCarousel = () => {
  const [imageUrls, setImageUrls] = useState<string[]>([]);
  const storage = useStorage();

  const getImages = async () => {
    const storageRef = ref(storage, 'Tech/');
    const files = await listAll(storageRef);

    const imageAppender: string[] = Array(files.items.length);
    console.log('img', imageAppender.length);
    files.items.forEach(async (item, key) => {
      const imageRef = ref(storage, item.fullPath);
      getDownloadURL(imageRef).then((url) => {
        imageAppender[key] = url;
        setImageUrls(imageAppender);
      });
    });
  };

  useEffect(() => {
    getImages();
  }, []);

  return (
    <>
      <div className="carousel w-full">
        {imageUrls.map((image, key) => {
          return (
            <div key={key} id={'item'   key} className="carousel-item w-full">
              <img src={image} className="w-full" />
            </div>
          );
        })}
      </div>
      <div className="flex justify-center w-full py-2 gap-2">
        {imageUrls.map((_, key) => {
          return (
            <a key={key} href={'#item'   key} className="bth btn-ghost btn-circle text-center">
              {key}
            </a>
          );
        })}
      </div>
    </>
  );
};

export default MyCarousel;

CodePudding user response:

Problem is with the way you update your imageUrls state. You always pass same imageAppender in your setImageUrls, so in that case DOM is re-rendered only after first setImageUlrs state update.

files.items.forEach(async (item, key) => {
    const imageRef = ref(storage, item.fullPath);
    getDownloadURL(imageRef).then((url) => {
      setImageUrls(currentUlrs => {
        const updatedUrls = [...currentUlrs];
        updatedAppender[key] = url;
        return updatedAppender;
      });
    });
  })

Please read useState functional updates - to get latest state on update. So basically you need to pass new reference to new array (create updatedAppender) to trigger re-render.

In addition It is better to not use forEach with async tasks. Please read this thread for alternatives What is the difference between async/await forEach and Promise.all map


  • Related