15

I'm trying to implement a feature in my app where when the user tries to navigate away from an un-submitted form, they're given an confirm that asks them if they're sure they want to leave before saving their changes.

The componentWillUnmount method seems like perfect candidate for this because it will fire for all the various ways that a user may abandon the form (changes a parent component state that causes it to disappear, navigates to a different route, etc...). However... I have no way to prevent the unmount when the confirm returns false.

Any suggestions on how I can go about implementing this?

  • 1
    Possible duplicate of [How can I prevent React from unmounting/remounting a component?](http://stackoverflow.com/questions/33151563/how-can-i-prevent-react-from-unmounting-remounting-a-component) – orvi May 07 '17 at 05:49
  • I'm sure you can use [`window.beforeunload`](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload) function – KA01 May 07 '17 at 05:54
  • 2
    No, you can't! You only update components on the view! Only will we work if you update the browser route, not the params after # – Leo Gasparrini Feb 14 '18 at 03:59

3 Answers3

12

This is best handled via react-router: setRouteLeaveHook.

componentWillMount() {
    this.unregisterLeaveHook = props.router.setRouteLeaveHook(props.route, this.routerWillLeave.bind(this));
}

routerWillLeave(nextLocation) {
  return false;        
}

And when component is unmounted, unregister the leave hook:

componentWillUnmount() {
    this.unregisterLeaveHook();
}
vijayst
  • 20,359
  • 18
  • 69
  • 113
12

Using react-router you can easily prevent route change(which will prevent component unmount) by using Prompt.

import { Prompt } from 'react-router';

const BlockingComponent = ({ isBlocking }) => (
  <div>
    <Prompt
      when={isBlocking}
      message={location =>
        `Are you sure you want to go to ${location.pathname}`
      }
    />
    {/* Rest of the JSX of your component */}
  </div>
);

Here isBlocking should be a bool indicating whether a blocking prompt should be shown or not. You can find a complete demo on react-router website

This method will work for BrowserRouter and HashRouter, but if you are using MemoryRouter, you need to create your Router as shown below then use Prompt in your components:

<MemoryRouter
  getUserConfirmation={(message, callback) => {
    callback(window.confirm(message));
  }}
>
  {/* Your App */}
</MemoryRouter>

You need to manually pass the getUserConfirmation prop which is a function.
You can modify this function as you like in any Router(Browser, Memory or Hash) to create your custom confirmation dialog(eg. a modal) instead of using the browser's default confirm.

Vaibhav Vishal
  • 6,576
  • 7
  • 27
  • 48
  • 1
    It performs a URL deffect - when the Prompt window is appearing - it changes URL string to previous one. Then you're clicking Cancel button and there comes a surprise - URL can be changed back or NOT. But you're staying on your current page, and that's okay. This problem's coming from 2017 and seems like still not resolved – Ihor Jun 09 '20 at 11:15
0

I wouldn't recommend doing that in componentWillUnmount. I usually want to do that in the store (if you are using flux architecture). It is usually the store that needs to keep track of all the data. The componentWillUnmount function is were you would want to unmount the store listener.

Demon
  • 826
  • 7
  • 22