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
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)
},
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.
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.
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.
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