Skip to content

Instantly share code, notes, and snippets.

@mknabe
Last active September 18, 2021 09:14
Show Gist options
  • Save mknabe/bfcb6db12ef52323954a28655801792d to your computer and use it in GitHub Desktop.
Save mknabe/bfcb6db12ef52323954a28655801792d to your computer and use it in GitHub Desktop.
React component to warn users about unsaved changes to forms when they attempt navigate away from the page using redux-form

Use with the following

  • react-router
  • redux-form

You must use this compontent on the route component where the form is shown not on the form component itself. This is because this.props.route is only added to route components, and I didn't want to pass that down through props.

Inspired by this Codepad

Feedback appreciated

To Do

  • Accept an array of form names
export default warnAboutUnsavedChanges(
connect(
mapStateToProps,
mapDispatchToProps
)(Component),
'clientContactsForm'
);
import React, {PropTypes} from 'react';
import {withRouter} from 'react-router';
import {connect} from 'react-redux';
import {isDirty} from 'redux-form';
function warnAboutUnsavedChanges(WrappedComponent, formName) {
class WarnAboutUnsavedChanges extends React.Component {
componentDidUpdate() {
this._promptUnsavedChange(this.props.isFormDirty);
}
componentWillUnmount() {
window.onbeforeunload = null;
}
_promptUnsavedChange(isUnsaved = false, leaveMessage = 'Leave with unsaved change?') {
// Detecting page transition (prevent leaving by setting true)
// for React Router version > 2.4
this.props.router.setRouteLeaveHook(this.props.route, () => isUnsaved && confirm(leaveMessage));
// Detecting browser close
window.onbeforeunload = isUnsaved && (() => leaveMessage);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
WarnAboutUnsavedChanges.propTypes = {
isFormDirty: PropTypes.bool,
// react-router
router: PropTypes.object,
route: PropTypes.object
};
const mapStateToProps = (state) => ({
isFormDirty: isDirty(formName)(state)
});
return withRouter(connect(
mapStateToProps,
null
)(WarnAboutUnsavedChanges));
}
export default warnAboutUnsavedChanges;
@prakashsvmx
Copy link

+1

@aishek
Copy link

aishek commented Aug 30, 2018

My version for Router v.4

import React, { Fragment } from "react"
import PropTypes from "prop-types"
import { withRouter, Prompt } from "react-router"
import { connect } from "react-redux"
import { isDirty } from "redux-form"

function warnAboutUnsavedForm(WrappedComponent, formName) {
  class WarnAboutUnsavedChanges extends React.Component {
    static propTypes = {
      isFormDirty: PropTypes.bool,
      leaveMessage: PropTypes.string.isRequired
    }

    static defaultProps = {
      leaveMessage: "Leave with unsaved change?"
    }

    componentDidUpdate() {
      this._promptUnsavedChange(this.props.isFormDirty, this.props.leaveMessage)
    }

    componentWillUnmount() {
      window.onbeforeunload = null
    }

    _promptUnsavedChange(isUnsaved = false, leaveMessage) {
      window.onbeforeunload = isUnsaved && (() => leaveMessage)
    }

    render() {
      return (
        <Fragment>
          <WrappedComponent {...this.props} />
          <Prompt
            when={this.props.isFormDirty}
            message={this.props.leaveMessage}
          />
        </Fragment>
      )
    }
  }

  const mapStateToProps = state => ({
    isFormDirty: isDirty(formName)(state)
  })

  return withRouter(connect(mapStateToProps, null)(WarnAboutUnsavedChanges))
}

export default warnAboutUnsavedForm

@davidfurlong
Copy link

These solutions don't seem to handle navigation post submitting state/during form submit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment