1

I'm trying to achieve the slider functionality, where I want to slider to move to the next question after every three seconds [this part is working fine]

Features i'm trying to achieve:

  • OnMouseEnter: Timer stops, css animation stops(working)
  • OnMouseleave: Start the timer from remaining time left
  • OnClick: Change Question number to the selected items (working) and start animation

The issue is when I changed the question_number by onClick event, the next question start showing after every 1 seconds instead of 3 seconds. I think the problem is somehow with the React Re-rendering.

setSlider is simple useState has object:

const [timeSlider, setTimeSlider] = useState({
  question_length: 4,
  question_number: 0,
  current_time: 0
});

I'm using useEffect to check the viewport width so react Re-render the components everytime, Viewport changes

I tried to store the Interval for timer in a react useRef. but still not working. Here is my code:

I use useRef, so I can Resume, Pause and updateNumber outSide where the first time setInterval started const interval = useRef(null);

After EverySecond update the useState: Seconds

useEffect(() => {
  setRemaining({
    start_time: Date.now(),
    stop_time: 0
  });
  interval.current = setInterval(() => {
    setSeconds(seconds => seconds + 1);
  }, 1000);
  pauseSlider();
  return () => clearInterval(interval);
}, []);

And when ever the seconds state changed, I make sure after every three seconds. question_number increased only when its less than 3

useEffect(() => {
  if (seconds === 3) {
    setSeconds(seconds => 0);
    setTimeSlider(prev => prev.question_number === 3 ? { ...timeSlider,
      question_number: 0
    } : { ...timeSlider,
      question_number: timeSlider.question_number + 1
    });
  }
}, [seconds]);

Below are the pause, resume and update function.

function pauseSlider() {
  setSliderHover(true)
  setRemaining({
    start_time: 0,
    stop_time: Date.now()
  });
  clearInterval(interval.current);
}

function resumeSlider() {
  setSliderHover(false)
  setRemaining(prev => ({ ...prev,
    start_time: prev.stop_time,
    stop_time: 0
  }));
  interval.current = setInterval(() => {
    setSeconds(seconds => seconds + 1);
  }, 1000);
}

function updateNumber(num) {
  setSliderHover(false)
  setSeconds(0)
  setRemaining({
    start_time: 0,
    stop_time: 0
  });
  clearInterval(interval.current);
  interval.current = setInterval(() => {
    setSeconds(seconds => seconds + 1);
  }, 1000);
  setTimeSlider({ ...timeSlider,
    question_number: num
  });
}

The Problem could be in these functions, I tried to use setTimeout instead of setInterval same results.

Below is the picture of slider, so you have more clear idea what i'm trying to achieve.

Demo of what i'm trying to achieve, the current question_number is 3 (questions starts from 0)

Layhout
  • 1,445
  • 1
  • 3
  • 16
Tahir Musharraf
  • 97
  • 3
  • 11
  • I tried to achieve something similar using Rxjs you might benefit from https://stackoverflow.com/questions/64711926/in-rxjs-how-to-pause-a-stream-of-data-and-continue-from-the-second-it-was-paused – Oguzhan Dec 06 '22 at 07:01
  • when you clear interval, you need to clear `interval.current`. like this `return () => clearInterval(interval.current);` – Layhout Dec 06 '22 at 07:39
  • @Layhout Thanks for the comment but I changed it and did get the expected result – Tahir Musharraf Dec 06 '22 at 09:02

1 Answers1

0

Instead of using the JS, I used the GSAP for this here is my solution for it

useLayoutEffect(() => {
    const ctx = gsap.context((self) => {
      TimelineRef.current = gsap.timeline({
        defaults: {
          repeat: -1,
          ease: "none",
        }
      })
        
      .to(".timerSlier_left", 
      {
        x: "0%",
        duration: 10,
        onRepeat: () => UpdateToNextQuestion(timeSlider),

      }
      )
    }, ContainerRef); // <- Scope!
    return () => ctx.revert(); // <- Cleanup!
  }, []);

  
  const [ remaining, setRemaining ] = useState(0);
  
  useEffect(()=>{
    if ( isInViewport ){
      resumeSlider()
    }else{
      pauseSlider()
    }
  }, [ isInViewport ]);
  const UpdateToNextQuestion = () => {
    settimeSlider( prev => prev.question_number === 3 ? ({...prev, question_number: 0}) : ({...prev, question_number: prev.question_number+1}))
  }

  function pauseSlider(){
    console.log("stop slider")
    setSliderHover(true)
    TimelineRef.current.pause();
  }
  function resumeSlider(){
    setSliderHover(false)
    TimelineRef.current.resume();
  }
  function updateNumber(num){
    TimelineRef.current.restart();
    settimeSlider( timeSlider => ({ ...timeSlider, question_number: num }) );
    console.log("RESUMING")
    setSliderHover(false)
    TimelineRef.current.resume();
  }

I'm handing timeout functionality from GSAP timeline.

Tahir Musharraf
  • 97
  • 3
  • 11