react-router中的自定义离开对话框

IT技术 reactjs react-native react-router
2021-05-08 12:25:16

我有一个表单,用户可以在其中编辑内容。如果用户有一些未保存的更改并想离开页面,我想给他一个自定义对话框,询问他是否确定。如果然后导航应该继续并且表单更改标志应该被重置,否则用户应该停留在页面上。

在这里您可以检查我当前正在检查此条件的高阶组件。它正在工作,但该函数leave返回一个字符串,然后 react-router 将在本机警报中显示该文本。

有没有办法在这里显示我的自定义对话框?并且我还可以获得回调或类似的警报,以便我可以调度一个事件,该事件告诉存储,最新的更改并不重要。

import React, {Component, PropTypes} from "react";
import connectRouter from "./connectRouter";
import { connect } from "react-redux";
import {injectIntl, intlShape} from "react-intl";

export default function confirmLeave(RouteTargetComponent) {

  @injectIntl
  @connectRouter
  @connect((state, routing) => {
    return {
      studies: state.studies
    };
  })
  class ConfirmLeaveHOC extends Component { // HOC = Higher Order Component

    static propTypes = {
      router: PropTypes.object.isRequired,
      route: PropTypes.object.isRequired,
      dispatch: PropTypes.func.isRequired,
      intl: intlShape.isRequired,
      studies: PropTypes.object.isRequired,
    };

    leave = () => {
      if (this.props.studies.isChanged) {
        // lets stop the navigation
        return this.props.intl.formatMessage({ id: "confirmLeave" });
      }
      // continue the navigation
      return true;
    }

    componentDidMount() {
      this.props.router.setRouteLeaveHook(this.props.route, this.leave.bind(this));
    }

    render() {
      // render the component that requires auth (passed to this wrapper)
      return (<RouteTargetComponent {...this.props}/>);
    }
  }

  return ConfirmLeaveHOC;
}
2个回答

由于无法自定义浏览器对话框,因此您必须呈现一个单独的组件(例如引导模式)并使用回调来确定单击了哪个按钮以及要执行的操作。

我实际上遇到了你最近面临的同样问题,我能够通过使用routerWillLeave和使用来自另一个组件的回调来解决它

表单组件

routerWillLeave = (route) => {
  if (!this.waitingForConfirm && this._hasUnsavedChanges() && !this.clickedSave) {
    this.refs.confirmAlert._show( ((v) => {
      if (v) {
        this.context.router.push(route.pathname);
      }
      this.waitingForConfirm = false;
    }).bind(this));
    this.waitingForConfirm = true;
    return false;      
  }
}

不幸的是,像这样的自定义对话框的实现是一件很痛苦的事情。我必须在这里使用 3 个变量来正确控制所需的行为:

  • waitingForConfirm- 当用户确认导航时,防止逻辑第二次运行是必要的。具体来说,当回调运行并且我们执行时this.context.router.push(route.pathname)routerWillLeave将再次运行(!),但由于我们已经确认导航,因此必须防止此逻辑再次运行。

  • _hasUnsavedChanges() - 检查是否有任何输入字段已更改(没有理由询问是否没有要保存的更改)。

  • clickedSave- 如果用户点击,不要要求确认Save- 我们知道我们想离开。

对话框组件

_show = (callback) => {
  this.callback = callback;
  this.setState({show: true});
}

_hide = () => {
  this.setState({show: false});
  this.callback = null;
}

_dialogAction = (input) => {
  if (this.callback) {
    this.callback(input);
  }
  this._hide();
}

render() {
  return (
    ...
    <Button onClick={this._dialogAction.bind(this, true)}>Yes</Button>
    <Button onClick={this._dialogAction.bind(this, false)}>No</Button>
  );
}

显然,您必须自定义上述代码段以适合您的应用程序,但希望它能够提供有关如何解决问题的一些见解。

一种不太复杂且更先进的方法是在您的组件上使用 setRouteLeaveHook。按照react-router v2.4.0

 
    import React from 'react'
    import { withRouter } from 'react-router'

    const Page = React.createClass({

        componentDidMount() {
            this.props.router.setRouteLeaveHook(this.props.route, () => {
               if (this.state.unsaved)
                 return 'You have unsaved information, are you sure you want to  
                 leave this page?'
           })
         }

         render() {
            return Stuff
         }

     })

     export default withRouter(Page)

这是他们 github 页面上的 React-router 版本