skip to Main Content

What is the proper way to do that? My goal is to save in database the count of minutes of the video the user has watched until he closed the page and set the video’s minutes to where he left off, next time he opens the very same video. I’ve tried to do that with beforeunload event but the page is closing before the function return. I’m using async/await but I don’t know how to make sure the event finish to run properly. I’ve tried something like this in my react component:

const ec = async (e) => await handleBeforeUnload(e);
    window.addEventListener('beforeunload', ec);

    return () => {
        window.removeEventListener('beforeunload', ec);
    }

but doesn’t gurante that the function finish before leaving the page. What is the proper way to do that?

edit: here’s the rest of the code:

    const handleBeforeUnload = async (event) => {
        await save_timestamp();
    }

    const save_timestamp = async () => {

    const v:IResumeVideo = {
        seconds: timeline.current,
        video_id: video_id
    }

    const response = await fetch('/api/save_video', {
        method: 'POST',
        body: JSON.stringify(v)
      });
    const result = await response.json();           
}

3

Answers


  1. It seems that using beforeunload is not recommended at all for your using case of saving the user’s state, see: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event:

    The beforeunload event suffers from some problems: It is not reliably fired, especially on mobile platforms. For example, the beforeunload event is not fired at all in the following scenario:
    A mobile user visits your page.
    The user then switches to a different app.
    Later, the user closes the browser from the app manager.

    Note: It is recommended to use the visibilitychange event as a more reliable signal for automatic app state saving that gets around problems like the above. See Don’t lose user and app state, use Page Visibility for more details.

    EDIT: I’ve briefly tested in a fiddle what happens if you have a async function on the visibilitychange, and it seems to be reliably fired. The code snippet in question is:

    document.addEventListener('visibilitychange', async () => {
        if (document.visibilityState === 'hidden') {
            await timeout(1000); // this would be your async api call
            console.log('was hidden!');
        }
    });
    
    function timeout(s) {
        return new Promise(r => {
        setTimeout(r, s);
      });
    }
    
    Login or Signup to reply.
  2. You can create a dedicated hook with sendBeacon as Elvis mentioned in the comment, in this way you can use also for different cases in different components without duplicating same code

    const useUnloadBeacon = ({ beaconUrl, fetchUrl, data }) => {
      const handleUnload = async () => {
        const jsonData = JSON.stringify(data);
    
        navigator.sendBeacon(beaconUrl, jsonData);
    
        try {
          const response = await fetch(fetchUrl, {
            method: 'POST',
            body: jsonData,
            headers: {
              'Content-Type': 'application/json',
            },
          });
    
          const result = await response.json();
        } catch (error) {
          console.error(error);
        }
      };
    
      useEffect(() => {
        window.addEventListener('unload', handleUnload);
    
        return () => {
          window.removeEventListener('unload', handleUnload);
        };
      }, [beaconUrl, fetchUrl, data]);
    };
    
    export default useUnloadBeacon;
    
    Login or Signup to reply.
  3. There is a web API, the Beacon API, to do exactly that and guarantee the data is sent even after the user leaves the page:

    https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API

    document.addEventListener("visibilitychange", function logData() {
        if (document.visibilityState === "hidden") {
            navigator.sendBeacon("/log", analyticsData);
        }
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search