Search code examples
javascriptreactjsnext.jsuse-stateclassname

React JS update classes when useState is updated


I am trying to make a carousel slider in react. Now am I checking the state and if the state is 2 for example add the tailwind class -left-1/3. Now that is working all fine. But only I have to click twice before it adds the first switch case. Does anyone know if there is a way (maybe with useEffect) to update or reload when the stae is getting updated?

const Hero = () => {
  const [sliderIndex, setSliderIndex] = useState(1);
  const [sliderClass, setSliderClass] = useState("-left-0");

  console.log(sliderIndex);
  const nextSlide = () => {
    setSliderIndex(sliderIndex + 1);
    console.log("onclick", sliderIndex);

    switch (sliderIndex) {
      case 1:
        setSliderClass("-left-0");
        break;
      case 2:
        setSliderClass(`-left-1/${dataSlider.length}`);
        break;
      case 3:
        setSliderClass(`-left-2/${dataSlider.length}`);
        break;
    }
    if (sliderIndex >= dataSlider.length) {
      setSliderIndex(1);
    }
  };
  const prevSlide = () => {
    console.log("onclick", sliderIndex);

    setSliderIndex(sliderIndex - 1);
    switch (sliderIndex) {
      case 1:
        setSliderClass("-left-0");
        break;
      case 2:
        setSliderClass(`-left-1/${dataSlider.length}`);
        break;
      case 3:
        setSliderClass(`-left-2/${dataSlider.length}`);
        break;
    }
    if (sliderIndex < 2) {
      setSliderIndex(3);
    }
  };

  useEffect(() => {
    console.log("change");
  }, [sliderIndex]);

  return (
    <header className=" w-screen h-[90vh] relative ">
      <div className="flex w-[300%]  h-full">
        {dataSlider.map((obj, index) => {
          return (
            <>
              <div
                className={`${sliderClass} transition-all duration-700 relative w-full h-full`}
              >
                <img
                  // layout="fill"
                  // objectFit="cover"
                  src={`/sliderImg${index + 1}.jpg`}
                  alt={obj.aboutImg}
                  className={`  object-cover  transition-all  duration-1000 h-full w-full`}
                />
                <div className="absolute left-4 md:left-20 bottom-8">
                  <h3 className="text-h2 text-primary font-[magilio] leading-none">
                    {obj.title}
                  </h3>
                  <p className="text-white w-[70%] mb-4">{obj.text}</p>
                  <Link href={obj.btnHref}>
                    <button className=" px-8 py-4 text-white rounded active:scale-[0.98] hover:scale-[1.02] transition-transform bg-primary ">
                      {obj.btnText}
                    </button>
                  </Link>
                </div>
              </div>
            </>
          );
        })}
        <button
          onClick={prevSlide}
          className="absolute md:pl-[18px] pl-[9px] rounded md:w-12 md:h-12 w-8 h-8 z-10 active:scale-[0.98] hover:scale-[1.02] transition-transform cursor-pointer top-0 bottom-0 left-0 mt-auto mb-auto ml-4 md:ml-20 bg-white"
        >
          <svg
            width="10"
            height="19"
            viewBox="0 0 10 19"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <rect
              width="12.368"
              height="1.76686"
              transform="matrix(0.707425 0.706788 -0.707425 0.706788 1.25 8.18359)"
              fill="#111731"
            />
            <rect
              width="12.368"
              height="1.76686"
              transform="matrix(-0.707425 0.706788 -0.707425 -0.706788 10 1.92017)"
              fill="#111731"
            />
          </svg>
        </button>
        <button
          onClick={nextSlide}
          className="absolute md:pl-[18px] rotate-180 pl-[9px] rounded md:w-12 md:h-12 w-8 h-8 z-10 active:scale-[0.98] hover:scale-[1.02] transition-transfom cursor-pointer top-0 bottom-0 right-0 mt-auto mb-auto mr-4 md:mr-20 bg-white"
        >
          <svg
            width="10"
            height="19"
            viewBox="0 0 10 19"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <rect
              width="12.368"
              height="1.76686"
              transform="matrix(0.707425 0.706788 -0.707425 0.706788 1.25 8.18359)"
              fill="#111731"
            />
            <rect
              width="12.368"
              height="1.76686"
              transform="matrix(-0.707425 0.706788 -0.707425 -0.706788 10 1.92017)"
              fill="#111731"
            />
          </svg>
        </button>
      </div>
    </header>
  );
};

export default Hero;```



Solution

  • @yainspan is correct in his comment. To explain with a bit of code:

    Try to avoid calling setState willy-nilly. I've removed an unnecessary call in this version of prevSlide():

    const prevSlide = () => {
        console.log("onclick", sliderIndex);
        let tempIndex = sliderIndex -1;
        if (tempIndex < 2) {
           setSliderIndex(3);
        }else{
           setSliderIndex(tempIndex);
        }
    };
    

    I've also remove the switch statement as, as yainspan suggests, you can just use sliderIndex to determine what class to add (you'll have to expand on the simplified version here but hopefully the logic is clear):

    <div
        className={`${sliderIndex === 0 ? '-left-0' : sliderIndex === 1 ? '-left-1' : sliderIndex === 2 ? '-left-2' : '' }transition-all duration-700 relative w-full h-full`}
              >