-1

Hello i am struggling to set dynamic time for settimeout function in react js. i have long string of key value pair of time and message. i wants to display each message for specific time and loop through whole list. here is what i am trying, but not working.

const [timer, setTimer] = useState(0)
   const [time, setTime] = useState(5000)// this is default value to start which need to update  with str time value

const str=[{name:"rammy", time:1000},
             {name:"james", time:4000},
             {name:"crown", time:2000}]
            useEffect(()=>{
            const getTime= str[timer].time
            setTime(getTime)

          },[timer]) 
//when timer change it should update  update time state which will be used to update time for time settime out
 
function increment() {  
  useEffect(()=>{    
setTimeout(() => {
  setTimer((ele)=>ele+1)
  
 }, time);
},[timer])
} // above code is for increment time state on each iteration

function ButtonHandle(){
  //setRealString(itr)
  increment()
  
} //button handler for start  timer
Preet Singh
  • 75
  • 2
  • 10

2 Answers2

3

First of all, you can't put hooks inside functions (other than your component functions). https://reactjs.org/docs/hooks-rules.html

So take the useEffect out of increment()

useEffect(()=>{
    increment()
},[timer])

function increment() {  
    setTimeout(() => {
        setTimer((ele)=>ele+1)
    }, time);
}

But you also need to clear the timeout. We can return the timeout function to reference it, and clear the time out with a return inside useEffect. Clearing timeouts and intervals in react

useEffect(()=>{
    const myTimeout = increment()

    return () => {
        clearTimeout(myTimeout)
    }
},[timer])

function increment() {

    return setTimeout(() => {
        setTimer((ele) => ele + 1);
    }, time);
}

Then we can combine the useEffects which both have a dependancy array of [timer].

useEffect(() => {
    const getTime = str[timer].time;
    setTime(getTime);

    const myTimeout = increment();

    return () => {
        clearTimeout(myTimeout);
    };
}, [timer]);
Jash1395
  • 228
  • 1
  • 6
  • I belive your explanation is better than mine. – JmLavoier Jan 12 '23 at 15:09
  • hi @jash1395 your code partialy works. somehow it incerment timer by skiping 1 iteration everytime and it starts as i refresh rather than wait for button click. i mean i wants it to start first on click and then keep going through whole list.please see codesandbox. [link ] (https://codesandbox.io/s/list-keepr-app-forked-vqwten) – Preet Singh Jan 12 '23 at 15:15
  • What you linked seems very different from my example. Here is a full working example, to get you going. (needs refactoring, but you should get the gist) https://codesandbox.io/s/confident-greider-w6imce?file=/src/App.js – Jash1395 Jan 12 '23 at 15:53
  • @jash1395 thanks for code.that is what I want to achieve. I need to learn more about hooks. – Preet Singh Jan 12 '23 at 16:02
1

You don't need to use useEffect to do it. You misunderstood the useEffect usage, it's a react hook to you implement side-effects and you can't use react-hooks inside a function, it should be in the component scope.

I can increment directly from the ButtonHandle function.

// On the index state is necessary in this implementation
const [index, setIndex] = useState(-1)

const guys=[
  {name: "rammy", time:1000},
  {name: "james", time:4000},
  {name: "crown", time:2000}
]

// useCallback memoize the increment function so that it won't
// change the instance and you can use it in the useEffect 
// dependency array
const increment = useCallback(() => {
  setIndex((i) => i+1)
}, [])

useEffect(() => {
   // change the state if the timer is greater than -1
   if (index !== -1) {
     if (index >= guys.length) {
       setIndex(-1);
     } else {
       setTimeout(() => {
         increment();
       }, guys[index].time); // <-- you get the time from your array
     }
   }
 }, [index, increment]);

 function handleClick(){
   //setRealString(itr)
   increment()
 }

Even though I helped you, I don't know what you're trying to do. This implementation sounds like a code smell. We can help you better if you explain the solution you're trying to do instead of just the peace of code.

You don't need to set the time state, as you already have the time in the array; avoiding unnecessary state changes is good.

JmLavoier
  • 523
  • 5
  • 9
  • bro, actully my final goal is to fetch subtitle SRT file and then animate all subtitles with timming which will be exported as mp4 file on later stage. – Preet Singh Jan 12 '23 at 15:22
  • @PreetSingh Your problem is simple, but you've made it complicated, so create a question with your true goal. – Tachibana Shin Jan 13 '23 at 01:41