reactjsembla-carousel

Error in react embla carousel when loading navigation arrows and dots


I have a carousel armed with embla that at the time of the first load of my site does not show me the navigation arrows (left and right) and the dots at the bottom (it only shows me one, not as many as it should because of the number of images). When reloading everything is displayed correctly

I don't know if it's a problem with how the images are loaded, which is done correctly because by moving with the mouse I can navigate the carousel.


export const CarouselPropiedad = () => {
  const [emblaRef, emblaApi] = useEmblaCarousel();
  const [canNext, setCanNext] = useState(false); // Cambiado a false inicialmente
  const [canPrev, setCanPrev] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(0);
  const [scrollSnaps, setScrollSnaps] = useState([]);
  const [images, setImages] = useState([]);

  const scrollPrev = useCallback(() => {
    if (emblaApi) emblaApi.scrollPrev();
  }, [emblaApi]);

  const scrollNext = useCallback(() => {
    if (emblaApi) emblaApi.scrollNext();
  }, [emblaApi]);

  const onSelect = useCallback(() => {
    if (!emblaApi) return;
    setSelectedIndex(emblaApi.selectedScrollSnap());
    setCanPrev(emblaApi.canScrollPrev());
    setCanNext(emblaApi.canScrollNext());
  }, [emblaApi]);

  // Método para cargar dinámicamente las imágenes
  const loadImages = async () => {
    const importedImages = [];
    for (let i = 1; i <= 5; i++) {
      const image = await import(`../assets/torre-promenade/torre-promenade-${i}.jpg`);
      importedImages.push(image.default);
    }
    setImages(importedImages);
  };

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

  useEffect(() => {
    if (emblaApi && images.length > 0) {
      emblaApi.reInit(); // Reinicializar Embla Carousel después de cargar las imágenes
      emblaApi.on("select", onSelect); // Establecer el evento select después de reinicializar
      onSelect(); // Llamar a onSelect inicialmente para establecer canPrev y canNext
      setScrollSnaps(emblaApi.scrollSnapList()); // Establecer los puntos de navegación
    }
  }, [emblaApi, images, onSelect]);

  return (
    <div className="embla rounded max-w-lg">
      <div className="embla__viewport" ref={emblaRef}>
        <div className="embla__container">
          {images.map((src, index) => (
            <div key={index} className="embla__slide">
              <img
                src={src}
                alt={`torre-promenade-${index + 1}`}
                className="w-max h-max aspect-square"
              />
            </div>
          ))}
        </div>
      </div>
      <div className="flex items-center justify-center gap-2 embla_dots_container z-10">
        {scrollSnaps.map((_, index) => (
          <DotButton
            key={index}
            selected={index === selectedIndex}
            onClick={() => emblaApi && emblaApi.scrollTo(index)} // Scroll to index
          />
        ))}
      </div>
      <button
        className="embla__button embla__button--prev disabled:hidden"
        onClick={scrollPrev}
        disabled={!canPrev}
      >
        <ChevronLeft />
      </button>
      <button
        className="embla__button embla__button--next disabled:hidden"
        onClick={scrollNext}
        disabled={!canNext}
      >
        <ChevronRight />
      </button>
    </div>
  );
};

I tried using another carousel that I have that if it works correctly, just replacing the code but it presents the same error. I also tried using a "loading" so that the carousel is displayed only when the images finish uploading, in case that was the problem, but it didn't work either


Solution

  • You didn't specify what version of Embla Carousel you're using but assuming you're using v8 and up, Embla automatically picks up when slides are added or removed and will re-initialise. You should be able to solve the problem by adding reInit event listeners and changing your carousel initialisation code into this:

      useEffect(() => {
        if (!emblaApi) return
    
        const updateScrollSnaps = (emblaApi) => setScrollSnaps(emblaApi.scrollSnapList());
    
        emblaApi
          .on("select", onSelect)
          .on("reInit", onSelect); // Run onSelect on reInit
          .on("reInit", updateScrollSnaps); // Update scrollSnaps on reInit 
    
        onSelect();
        updateScrollSnaps();
      }, [emblaApi]);