Stopping a timer in useEffect - reactjs

From what I can tell, the timer being called in a different scope.. how can I accomplish a function to stop the timer? Going a bit crazy here, thank you for any help.
const SomeComponent = ({ isPlaying }) => {
let timer = null;
React.useEffect(() => {
if (isPlaying) {
startTimer();
}
},[isPlaying]);
const startTimer = () => {
timer = setInterval(() => {
console.log('tick');
}, 1000);
};
const stopTimer = () => {
console.log('stopping timer: ', timer); // shows null, instead of giving the timerId to stop properly
clearInterval(timer);
};

The timer variable will "reset" each time your component is re-rendered. Even if it holds your timer, a re-render will set its value to null again.
You could either move out of the component scope, or use useRef to keep the variable through re-renders:
const SomeComponent = ({ isPlaying }) => {
const timer = React.useRef(null);
React.useEffect(() => {
if (isPlaying) {
startTimer();
}
return () => clearInterval(timer.current);
}, [isPlaying]);
const startTimer = () => {
timer.current = setInterval(() => {
console.log('tick');
}, 1000);
};
const stopTimer = () => {
clearInterval(timer.current);
};
Note that I also force a clearInterval by using a return inside the useEffect. This way the component will automatically "clean up" when it unmounts. I also changed timer to be a constant.

Related

Refactor a custom hook to be called inside a method

I created this hook that is responsible to do something after an amount of time:
const useTime = (callback, timeout = 1000) => {
useEffect(() => {
const timer = setTimeout(() => {
callback()
}, timeout);
return () => clearTimeout(timer);
}, []);
}
The hook is working, but i can not call it inside a method like:
{
clear: () => {
useTime(() => console.log('close'), 6000 )
},
... this is happen because of hooks rules. Question: How to refactor the hook to be able to call it inside a method or a function?
You probably need to do like this -
function useTime(cb, timeout = 100) {
const timer = setTimeout(() => {
cb();
}, timeout);
return () => clearTimeout(timer);
}
function anotherMethod() {
const cancel = useTime(runJob, 1000);
// if you wanna cancel the timer, just call the cancel function
cancel();
}
You can try something around this:
const useTime = () => {
const timer = useRef();
const fn = useCallback((callback, timeout = 1000) => {
timer.current = setTimeout(() => {
callback()
}, timeout);
}, []);
useEffect(() => {
return () => clearTimeout(timer.current);
}, []);
return fn;
}
const delayedFn = useTime();
clear: () => {
delayedFn(() => console.log('close'), 6000)
},

useeffect not running on first mount but runs after first reload

onStart is button and when i press it , it must run useeffect but it does not run it in first start but run it on reload. state value change on first start on dev tool.
const [started, setStarted] = useState(false);
const onStart = () => {
setStarted(true);
};
useEffect(() => {
if(started){
let timer = setInterval(() => tick(), 1000);
return () => clearInterval(timer);
}
}, []);
You need to add started to the dependency array of the useEffect.
const [started, setStarted] = useState(false);
const onStart = () => {
setStarted(true);
};
useEffect(() => {
if (started) {
let timer = setInterval(() => tick(), 1000);
return () => clearInterval(timer);
}
}, [started]); // Add started here.

Using useState and UseEffect in this way makes the slider for for the first times but then dosent work

const HeroSection = () => {
const [Change,setChange] = useState(false);
useEffect(() => {
setInterval(() => {
setChange(!Change)
}, 5000);
return () => clearInterval();
}, [Change]);
return (
<HeroContainer>
<HeroBg>
<ImageBg src={ Change ? Image1 : Image2 } />
</HeroBg>
</HeroContainer>
)``
}
export default HeroSection
react auto slide images works for the first few times then becomes buggy as in fast paced changes of images regardless of the 5 sec interval
clearInterval(); does nothing. You need to pass the created interval's ID to clearInterval for it to be cleared.
I also don't think the effect hook should have change as a dependency unless you use setTimeout instead.
const [change, setChange] = useState(false);
useEffect(() => {
const timeoutId = setTimeout(() => {
setChange(!change)
}, 5000);
return () => clearTimeout(timeoutId);
}, [change]);
to set a new timeout in the effect callback every time change changes.
Or:
const [change, setChange] = useState(false);
useEffect(() => {
const intervalId = setInterval(() => {
setChange(change => !change)
}, 5000);
return () => clearInterval(intervalId);
}, []);
to set an interval only once, when the component mounts.

Does this cleanup function in useEffect run even after the component is unmounted?

Right now I am calling an interval function for every 10s. My question is in the useEffect I have a dependency array that has channel id. So when the component unmounts does this clearInterval function gets called?
const pollCurrentConversationId = channelID => {
pollBackendStart({ metadata: { channelID } });
};
const pageFocused = () => {
if (document.hasFocus()) {
pollCurrentConversationId(channelID);
}
pollingTimerId.current = setInterval(() => {
if (document.hasFocus()) {
pollCurrentConversationId(channelID);
}
}, 10000);
};
const pageNotFocused = useCallback(() => {
if (channelID) {
pollCurrentConversationId(channelID);
}
clearInterval(pollingTimerId.current);
}, [channelID, pollBackendStart]);
useEffect(() => {
if (channelID) {
pageFocused();
}
return () => {
clearInterval(pollingTimerId.current);
};
}, [currentConversation.id]);
The cleanup runs when the component is unmounted, regardless of the dependency array. It also runs every time the component rerenders.

setInterval can't be stopped by clearInterval when use setState in React

const handleClick = () => {
setState({ ...state, on: !state.on });
let tog = state.on;
console.log("first" + tog);
const interval = setInterval(() => {
if (tog) {
console.log(tog);
} else clearInterval(interval);
}, 1000);
};
enter image description here
this one will not be able to stop even the tog is false;
however if I don't use state, change to a variable it will not happen,
it is so weird for me, I need some help;
let flag = true;
const handleClick = () => {
flag = !flag;
console.log("first" + flag);
const interval = setInterval(()=>{
if(flag){
console.log(flag);
}else(clearInterval(interval))
},1000)
};
React will create new handleClick on every re-renders, and there will be different setIntervals,
const intvl = useRef(null);
const handleClick = () => {
intvl.current = setInterval(() => { //now interval is not changing on every fn recreation
.....
clearInterval(intvl.current);
}
check this one
You should use an Effect to handle intervals, something like this:
useEffect(() => {
let intervalId
if (state.on) {
intervalId = setInterval(() => {
console.log(state.on)
}, 1000)
return () => clearInterval(intervalId)
}
}, [state.on])
const handleClick = () => {
setState({ ...state, on: !state.on });
};
working code

Resources