0

if i used setInterval(line 15) without useEffect than it gives result 2^n-1(0,1,3,7,15,31,63...) instead of(0,1,2,3,4,..) . so i have some question
1)why i am getting that output when I directly called setInterval without useeffect 2)is there any way if I change setCount(line 9) and its gives correct output by use setInterval directly without useEffect(as I did) 3) if the use of setInterval is not possible without useEffcet than why it is not possible?

if i put setInterval in useEffect and render initially once( line 12,13,14) than it gives correct output..... but I do not get the correct output when I use directly setInterval. what is diff bet them?
in both cases, I call setInterval once but the output is diff.

import React, {useEffect, useState } from 'react'
    
    export default function IncorrectDependency() {
    
    const [count,setCount]=useState(0)
    
    const inc=()=>{
        // console.log(count)
        setCount(preVal=>preVal+1)
    //    setCount(count+1)
    }
    // useEffect(()=>{
    //     setInterval(inc,1000)},[]
    // )
    setInterval(inc,1000)
    
        return (
            <div>
                <h1>{count}</h1>
               
            </div>
        )
    }
meet vaghsiya
  • 200
  • 12

3 Answers3

7

When we do a set state, functional components will re-execute from top to bottom, how ever when we use useState, useCallbacks etc.. they will not re-initialize as variables, functions,

So in this case, setInterval will re-initialize on each and every setCount, because of the state got changed,

step by step

  • in the 1st second there will be one setInterval, and call setCount and component is ready to rerender
  • when re-redering, start executing functional component from top-to-bottom it sees setInterval again and it will trigger it, so now we have two setIntervals
  • so on it will add multiple setIntervals on each second, because we don't clear it, so you should see the number printed in the browser will not take a second, but less than a second when time goes by.

You can achieve the expected result without useEffect by clearing the previous interval on each re-render which is happen due to setCount

create a variable to hold the set interval, code

const interval = null;
//this should be declare out side the component,
//because if we declare it inside the component it will redeclare,
//and the reference to the previous setInterval will be lost in that case no-way to clear the setInterval.
export default function IncorrectDependency() {
    ....
    if (interval) {
        clearInterval(interval);
    }

    interval = setInterval(inc, 1000);
    ....
 }

alternatively react has a hook which can hold the same variables without re-initializing on each renderings, check it out useRef

here is a code-demo

const intvl = useRef(null);
....

if (intvl?.current) {
    clearInterval(intvl.current);
}

intvl.current = setInterval(inc, 1000);
.....
Kalhan.Toress
  • 21,683
  • 8
  • 68
  • 92
  • 1
    Note that the correct way to do this is still `useEffect`. I'm clarifying for the next reader who might assume that this is the correct way to do intervals (it's not). – Dan Abramov Oct 04 '22 at 20:22
1

when you directly use setInterval what is happening as this is a function it will be called on state change so again a setInterval will be triggered and so on which actually give you the incorrect result, so you shouldn't use setInterval without use effect, also on unmount you should clearthe interval

Alok Takshak
  • 282
  • 1
  • 6
  • thank you your response. but i still did't get how setInterval will trigger again. i mean setInterval called function every second as per above example than how it trigger setInterval again? and you mean state change but react-dom change value only count than how and why it trigger setInterval? – meet vaghsiya Oct 09 '20 at 05:27
  • it is a function , react will call the function and when the function is called the line with setInterval will be trigered, you can put any console before setInterval and see on every state change the console will we called, in class component render function was called but in functional component whole function will run except hooks – Alok Takshak Oct 09 '20 at 05:48
-2

Dan Abramov explains why this isn't a good idea:

"it’s not the idiomatic way to do it. eg it won’t work correctly if you have multiple instances of the same component. it breaks the rules — it does a side effect (setInterval) during rendering, which the page said you should not do :) once you break the rules, all bets are off"

https://twitter.com/dan_abramov/status/1577395235340095501?s=20&t=nNnYyjLvHs5by_dqF5l8zg

frankie
  • 1
  • 1