Search code examples
javascriptreactjsnext.jsonbeforeunload

How to save data to database when the user request to leave?


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();           
}

Solution

  • 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 = ({ url, data }) => {
      const handleUnload = async () => {
        const jsonData = JSON.stringify(data);
    
        const sendBeaconSuccess = navigator.sendBeacon(url, jsonData);
        if(!sendBeaconSuccess) {
         try {
           const response = await fetch(url, {
             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);
        };
      }, [url, data]);
    };
    
    export default useUnloadBeacon;