skip to Main Content

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


  1. Chosen as BEST ANSWER

    Here is the complete working code in case it will help somebody afterwards.

    const SampleTestStats = () => {
    
      let SAMPLE_TEST_STATS_REFRESH_TIME = 3000;
      const [result, setResult] = useState([{ tid: 0, tcname: "none"},]);
      const intervalIDRef = React.useRef(null);
    
      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) {
              let newResult = {tid: res.data[0].tid, tcname: res.data[0].testname};
              setResult([newResult]);
            }
          });
        }catch(err) {
        }
      }
    
      useEffect ( () => {
        fetchTestStats();
        refreshPageInterval();
        return () => clearInterval(intervalIDRef.current);
      }, []); 
      
      function refreshPageInterval() {
        intervalIDRef.current =  setInterval(() => {
         window.location.reload()
        }, SAMPLE_TEST_STATS_REFRESH_TIME);
      }
    
      function stopTest(tid) {
        clearInterval(intervalIDRef.current);
        if(window.confirm(`You are about to stop the test, press OK to confirm.`)) {
          window.location.href = '/components/testbeds';
        } else {
          refreshPageInterval();
        }
      }
      
      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. I believe the problem you’re seeing is because your function wrapped with useCallback isn’t being updated when your sampleTimeOut 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 original null to your clearTimeout 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 using clearInterval.

    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:

    const SampleTestStats = () => { 
      ...
    
      let SAMPLE_TEST_STATS_REFRESH_TIME = 5000;
    
      function refreshPageInterval() {
        return setInterval(() => {
         window.location.reload()
        }, SAMPLE_TEST_STATS_REFRESH_TIME)
      }
    
      useEffect ( () => {
        fetchTestStats();
        const interval = refreshPageInterval();
        return () => clearInterval(interval);
      }, []); 
      
      ...
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search