I am trying to use a custom dialog to ask a user for confirmation before navigating away with unsaved data.
Following the docs I have:
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
But instead of
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
return 'You have unsaved information, are you sure you want to leave this page?'
}
I have
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
this.setState({open: true})
this.forceUpdatate() //necessary or else render won't be called to open dialog
}
The dialog component I am using comes from material-ui which just expects an open
boolean to control the dialog, it also takes a handleCancel
and handleContinue
methods, but I am not sure how to hook it up with routerWillLeave
.
The handleCancel
method is simple as it just closes the dialog:
handleCancel() {
this.setState({open: false})
};
I have wrapped the dialog component in component called Notification
export default class Notification extends React.Component {
render() {
const { open, handleCancel, handleContinue } = this.props
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={handleCancel}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={handleContinue}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={false}
open={open}
>
You have unsaved data. Discard changes?
</Dialog>
</div>
);
}
}
And I can call it from the parent component, I have this in the render method:
<Notification open={open} handleCancel={this.handleCancel} handleContinue={this.handleContinue}/>
Basically my question is how can I wire this up with routerWillLeave
instead of showing the native browser alert?
When you call createHistory
, one of the options to it is getUserConfirmation
, which takes a prompt message
and a callback
. For DOM histories (browserHistory
and hashHistory
), getUserConfirmation
calls window.confirm
, passing it the message
. The callback
function receives the return value of window.confirm
[0].
What you need to do is to provide your own getUserConfirmation
method that replicates window.confirm
. When it gets called, you should display your modal and trigger the callback
depending on which button is clicked.
The <Notification>
component should take the prompt message
and the callback
function to call based on the user's action.
class Notification extends React.Component {
contructor(props) {
this.state = {
open: false
}
}
handleCancel() {
this.props.callback(false)
this.setState({ open: false })
}
handleContinue() {
this.props.callback(true)
this.setState({ open: false })
}
render() {
const { message } = this.props
const { open } = this.state
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleCancel.bind(this)}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={this.handleContinue.bind(this)}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={true}
open={open}
>
{message}
</Dialog>
</div>
);
}
}
The confirmation modal isn't really a part of your UI, which is why I am rendering it in a separate render process than the rest of the app.
import React from 'react'
import ReactDOM from 'react-dom'
import Notification from './components/Notification'
export default function = (holderID) => {
var modalHolder = document.getElementById(holderID)
return function ModalUserConfirmation(message, callback) {
ReactDOM.render((
<Notification open={true} message={message} callback={callback} />
), modalHolder)
}
}
This will obviously force you to create your own history object. You cannot just import browserHistory
or hashHistory
because those use window.confirm
. Luckily it is trivial to create your own history. This is essentially the same code as is used in browserHistory
[1], but it passes createBrowserHistory
your getUserConfirmation
function.
import createBrowserHistory from 'history/lib/createBrowserHistory'
import createRouterHistory from './createRouterHistory'
export default function(getUserConfirmation) {
return createRouterHistory(createBrowserHistory({
getUserConfirmation
})
}
Finally, you need to put this all together.
import createHistory from './createConfirmationHistory'
import ModalConfirmation from './ModalConfirmation'
const getModalConfirmation = ModalConfirmation('modal-holder')
const history = createHistory(getModalConfirmation)
ReactDOM.render((
<Router history={history}>
// ...
</Router>
), document.getElementById('root')
You would have to refactor this a bit if you wanted to use a history singleton, but otherwise it should work. (I haven't actually tested it, though).
[0] https://github.com/mjackson/history/blob/v2.x/modules/DOMUtils.js#L38-L40
[1] https://github.com/ReactTraining/react-router/blob/master/modules/browserHistory.js