2

My form pages have a beforeunload event/function set on the window to warn the user to save their changes before moving on to a different page or refreshing. It works great, and looks something like this:

$(window).on("beforeunload", myCustomWindowBeforeUnloadFunction);

The problem is when running in development, every time I change a file in my IDE, Webpack Dev Server attempts to auto-reload the page. I want to turn off this beforeunload event so I don't keep getting nagged with this alert in Chrome:

Are you sure you want to leave the page? Data you have entered may not be saved.

Is there a way I can run a custom function in the browser before Webpack Dev Server attempts to reload the page?

Danziger
  • 19,628
  • 4
  • 53
  • 83
Ryan Shillington
  • 23,006
  • 14
  • 93
  • 108
  • you can catch the hot update event of webpack described here https://stackoverflow.com/questions/42868821/listen-for-hot-update-events-on-the-client-side-with-webpack-dev-derver and then remove that event by using removeEventListener – anees May 03 '20 at 17:43

1 Answers1

2

Maybe something like this:

if (process.env.DEV) {
  window.addEventListener('message', ({ data: { type } }) => {
    if (type === 'webpackInvalid') {
      $(window).off('beforeunload');
    } else if (type === 'INIT_INSTANCE') {
      $(window).on('beforeunload', myCustomWindowBeforeUnloadFunction);
    }
  });
}

I've added process.env.DEV with EnvironmentPlugin to make sure that code doesn't make it to production. You could also use process.env.NODE_ENV === 'development', which works without adding EnvironmentPlugin, as Webpack adds that automatically.

Or, using HMR's addStatusHandler function:

if (process.env.DEV && module.hot) {
  module.hot.accept();

  module.hot.addStatusHandler((status) => {
    if (status === 'prepare') {
      $(window).off('beforeunload');
    } else if (/apply|abort|fail/.test(status)) {
      $(window).on('beforeunload', myCustomWindowBeforeUnloadFunction);
    }
  });
}

Note module.hot will be there if you are using HotModuleReplacementPlugin, no need to import that, as explained here:

If Hot Module Replacement has been enabled via the HotModuleReplacementPlugin, its interface will be exposed under the module.hot property. Typically, users will check to see if the interface is accessible, then begin working with it.

Also, maybe instead of checking for apply, abort or fail in the second condition, you could use idle.

Danziger
  • 19,628
  • 4
  • 53
  • 83
  • 1
    YAS!! That first block worked. I don't have a `window.process` or a `window.module` so I'm not sure where that's supposed to come from. But I can just listen for "message" like you did above and disable it. That works perfectly. We don't use webpack-dev-server in prod so I'm pretty sure the `webpackInvalid` message will never be fired, right? – Ryan Shillington May 03 '20 at 18:11
  • There should not be any `webpackInvalid` messages in production, but it's a good practice to remove development-only code from your production builds, which is what those `if (process.env.DEV)` do. Anyway, I didn't notice I added that on my projects. The default way to do that check is `process.env.NODE_ENV === 'development'`. – Danziger May 03 '20 at 18:30
  • I have also added some additional details about `module.hot`. You should not import that, it will be exposed by `HotModuleReplacementPlugin`, so it should work as well, even though you might see some errors if using TypeScript. – Danziger May 03 '20 at 18:31
  • Wait. Where are you adding this code? On the server side somewhere? I put this in my js on the client/browser side. `process.env.*` is Node JS and `window.addEventListener` is in the browser, right? I feel like you have some crazy awesome method of injecting code in the browser through `webpack.config.js`? – Ryan Shillington May 03 '20 at 20:50
  • It's client-side. `process.env.*` is Node.js, which is where Webpack runs, so you have access to that in compilation time. For example, if you add `if (process.env.NODE_ENV === "development"` but then run a production build, that if and its content will be gone. – Danziger May 05 '20 at 21:32
  • 1
    If you want to inject additional compile-time properties to `process.env`, you should use [EnvironmentPlugin](https://webpack.js.org/plugins/environment-plugin/). If you want to inject additional compile-time properties that are not inside `process.env`, you should use [DefinePlugin](https://webpack.js.org/plugins/define-plugin/). – Danziger May 05 '20 at 21:34