I auto a refresh a page (say Page1) in every 5 second interval using setTimeout and callback function.
When I move to a new page (say Page2), its going new page and after few seconds again returning to Page1.
I tried window beforeunload event listener, react useBeforeUnload, nothing works. Here is the minimal code to reproduce the issue.
const SampleTestStats = () => {
let allowStateChange = true;
let sampleTimeOut = null;
let SAMPLE_TEST_STATS_REFRESH_TIME = 5000;
const [result, setResult] = useState([{ tid: 0, tcname: "none"},]);
const user = JSON.parse(localStorage.getItem('user'));
function refreshPage() {
sampleTimeOut = setTimeout(refreshPage, SAMPLE_TEST_STATS_REFRESH_TIME);
window.location.href = "/components/view/sampletest/stats";
}
useBeforeUnload(
React.useCallback(() => {
console.log('In useBeforeUnload');
allowStateChange = false;
clearTimeout(sampleTimeOut);
})
);
const fetchTestStats = async () => {
try {
const res = await axios.post('/test/current/active/data',
{uid: user.uid, email: user.email} ,
{headers: {
'Authorization': user.jwt,
}}).then(
function (res) {
if(res.status === 200) {
if(allowStateChange) {
let newResult = {tid: res.data[0].tid, tcname: res.data[0].testname};
setResult([newResult]);
}
}
});
}catch(err) {
}
}
useEffect ( () => {
fetchTestStats();
}, []);
sampleTimeOut = setTimeout(refreshPage, SAMPLE_TEST_STATS_REFRESH_TIME);
return (`My simple refresh test page, test id = ${result[0].tid} and test name = ${result[0].tcname}.`);
}
Can somebody please help me, when I move to a new page Page2 again automatically it should not come back to old page Page1.
EDIT to show the side impacts
I have a stopTest function, where it asks for user confirmation before moving to a new page (say Page3). This does not work. The complete code is as below.
const SampleTestStats = () => {
let allowStateChange = true;
let sampleTimeOut = null;
let SAMPLE_TEST_STATS_REFRESH_TIME = 3000;
const [result, setResult] = useState([{ tid: 0, tcname: "none"},]);
const user = JSON.parse(localStorage.getItem('user'));
const fetchTestStats = async () => {
try {
const res = await axios.post('/test/current/active/data',
{uid: user.uid, email: user.email} ,
{headers: {
'Authorization': user.jwt,
}}).then(
function (res) {
if(res.status === 200) {
/*if(allowStateChange)*/ {
let newResult = {tid: res.data[0].tid, tcname: res.data[0].testname};
setResult([newResult]);
}
}
});
}catch(err) {
}
}
useEffect ( () => {
fetchTestStats();
const interval = refreshPageInterval();
return () => clearInterval(interval);
}, []);
function refreshPageInterval() {
return setInterval(() => {
window.location.reload()
}, SAMPLE_TEST_STATS_REFRESH_TIME);
}
useBeforeUnload(
React.useCallback(() => {
allowStateChange = false;
})
);
function stopTest(tid) {
if(window.confirm(`You are about to stop the test, press OK to confirm.`)) {
window.location.href = '/components/testbeds';
}
}
return (
<div className="center"> My simple refresh test page, test id = {result[0].tid}
and test name = ${result[0].tcname}.<br/>
Stop the test <a href={'#'}>
<span onClick={() => stopTest(result[0].tid)}>here</span>
</a> now.</div>
);
}
2
Answers
Here is the complete working code in case it will help somebody afterwards.
I believe the problem you’re seeing is because your function wrapped with
useCallback
isn’t being updated when yoursampleTimeOut
values updates. This is because you aren’t passing a dependency array, so the callback won’t recompute any of its internals after it’s mounted. This means you’re passing the originalnull
to yourclearTimeout
function so your timeout isn’t being cleared properly.I have a few suggestions that could help.
First off, I recommend throwing all your refresh logic into your
useEffect
and calling the cleanup function to clear your timeouts. That way, whenever the component unmounts, it will clear all active timeouts.Also, there’s a handy
setInterval
function that will run a timeout but at a set interval that you define. You can clear this in a similar way usingclearInterval
.Finally, calling
window.location.reload()
is a cleaner way of reloading the page, and will be more modular, allowing you to use this component on any route.Something like this could work: