Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NextJS 14.2.4] Keen slider not update when a page is hydrated by Next/Link Component #439

Open
GabrielAtlas opened this issue Sep 2, 2024 · 1 comment

Comments

@GabrielAtlas
Copy link

GabrielAtlas commented Sep 2, 2024

Basically, the bug happens when you navigate through the next/Link component in an application made with NextJS where Keen Slider fails to update the slide. Watch the video as an example

2024-09-02.14-27-44.mp4

My Code:

'use client'
import { useEffect, useState } from 'react'
import Image from 'next/image'

import { useCardapio } from '@/app/contexts/cardapio-context'
import { useKeenSlider } from 'keen-slider/react'

import fallbackIcon from '../../public/icons/popeyes-fallback-category-icon.svg'

export default function CardapioFilters() {
  const { categories, isLoading } = useCardapio()

  const [currentSlide, setCurrentSlide] = useState(0)
  const [loaded, setLoaded] = useState(false)

  const [sliderRef, instanceRef] = useKeenSlider<HTMLDivElement>({
    initial: 0,
    slideChanged(slider) {
      setCurrentSlide(slider.track.details.rel)
    },
    created() {
      setLoaded(true)
    },
    slides: {
      perView: 9,
      spacing: 10,
    },
    breakpoints: {
      '(max-width: 500px)': {
        slides: {
          perView: 3,
        },
      },
    },
  })

  const [isFixed, setIsFixed] = useState<boolean>(false)
  const scrollThreshold = 360 // Height of the header is the threshold

  useEffect(() => {
    const handleScroll = () => {
      if (window.scrollY > scrollThreshold) {
        setIsFixed(true)
      } else {
        setIsFixed(false)
      }
    }

    window.addEventListener('scroll', handleScroll)

    return () => {
      window.removeEventListener('scroll', handleScroll)
    }
  }, [])

  useEffect(() => {
    setTimeout(() => instanceRef.current?.update(), 10) // FIX: Keen Slider Bug (On Switch between NextJS Link Navigation)
  }, [instanceRef])

  const shouldArrowAppears =
    loaded && categories.length > 1 && instanceRef.current
  const slideCount = instanceRef.current?.track?.details?.slides?.length ?? 0

  if (isLoading)
    return <div className="min-h-24 bg-neutral-200 animate-pulse w-full" />

  return (
    <div
      className={`min-h-24 bg-white w-full ${isFixed ? 'fixed top-0 md:top-[96px]' : 'block'}`}
    >
      <div className="mx-auto max-w-[1288px] px-6 md:px-0 w-full">
        <div className="navigation-wrapper">
          <div ref={sliderRef} className="keen-slider w-full pt-2">
            {categories?.map((category, index) => {
              return (
                <div
                  key={`cardapio-category-${index}`}
                  className="keen-slider__slide flex flex-col items-center gap-2 text-center"
                >
                  <Image
                    src={category.imagem?.url || fallbackIcon}
                    width={48}
                    height={48}
                    className="opacity-30"
                    alt="Plk Icon"
                  />
                  <span className="font-bold opacity-50">
                    {category.nome.length >= 14
                      ? category.nome.split(' ')[0]
                      : category.nome}
                  </span>
                </div>
              )
            })}
          </div>
          {shouldArrowAppears && (
            <div className="hidden md:block">
              <Arrow
                left
                onClick={(e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
                  e.stopPropagation()
                  if (instanceRef.current) instanceRef.current.prev()
                }}
                disabled={currentSlide === 0}
              />

              <Arrow
                onClick={(e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
                  e.stopPropagation()
                  if (instanceRef.current) instanceRef.current.next()
                }}
                disabled={currentSlide === slideCount - 1}
              />
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

function Arrow(props: {
  disabled: boolean
  left?: boolean
  onClick: (e: React.MouseEvent<SVGSVGElement, MouseEvent>) => void
}) {
  const disabled = props.disabled ? ' arrow--disabled' : ''
  const arrowDirection = props.left
    ? 'arrow-cardapio-left'
    : 'arrow-cardapio-right'

  return (
    <svg
      onClick={props.onClick}
      className={`arrow ${arrowDirection} ${disabled}`}
      xmlns="http://www.w3.org/2000/svg"
      width={40}
      height={40}
      viewBox="0 0 40 40"
    >
      {props.left && (
        <>
          <circle
            className="arrow-circle"
            cx="20"
            cy="20"
            r="20"
            fill="#E5E5E5"
          />
          <path
            className="arrow-path"
            d="M25.0019 10.9851C24.5119 10.4951 23.7219 10.4951 23.2319 10.9851L14.9219 19.2951C14.5319 19.6851 14.5319 20.3151 14.9219 20.7051L23.2319 29.0151C23.7219 29.5051 24.5119 29.5051 25.0019 29.0151C25.4919 28.5251 25.4919 27.7351 25.0019 27.2451L17.7619 19.9951L25.0119 12.7451C25.4919 12.2651 25.4919 11.4651 25.0019 10.9851Z"
            fill="#BDBDBD"
          />
        </>
      )}
      {!props.left && (
        <>
          <circle
            className="arrow-circle"
            cx="20"
            cy="20"
            r="20"
            fill="#E5E5E5"
          />
          <path
            className="arrow-path"
            d="M14.9982 29.0151C15.4882 29.5051 16.2782 29.5051 16.7682 29.0151L25.0782 20.7051C25.4682 20.3151 25.4682 19.6851 25.0782 19.2951L16.7682 10.9851C16.2782 10.4951 15.4882 10.4951 14.9982 10.9851C14.5082 11.4751 14.5082 12.2651 14.9982 12.7551L22.2382 20.0051L14.9882 27.2551C14.5082 27.7351 14.5082 28.5351 14.9982 29.0151Z"
            fill="#BDBDBD"
          />
        </>
      )}
    </svg>
  )
}
@yanckst
Copy link

yanckst commented Oct 12, 2024

I agree. I have the same problem using NextJs 14.2.13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants