代码之家  ›  专栏  ›  技术社区  ›  jsDev

如何防止用户离开当前路由?反应[重复]

  •  0
  • jsDev  · 技术社区  · 8 年前

    我希望我的ReactJS应用程序在离开特定页面时通知用户。特别是一条弹出消息,提醒他/她执行操作:

    更改已保存,但尚未发布。是否立即执行此操作

    我应该启动这个吗 react-router 这是全局性的,还是可以从react页面/组件中完成的?

    我还没有在后者身上找到任何东西,我宁愿避免第一个。除非这是正常的,但这让我想知道如何做到这一点,而不必为用户可以访问的其他可能的页面添加代码。。

    欢迎有任何见解,谢谢!

    0 回复  |  直到 8 年前
        1
  •  221
  •   jcady    5 年前

    react-router v4引入了一种阻止导航的新方法,使用 Prompt 。只需将其添加到要阻止的组件:

    import { Prompt } from 'react-router'
    
    const MyComponent = () => (
      <>
        <Prompt
          when={shouldBlockNavigation}
          message='You have unsaved changes, are you sure you want to leave?'
        />
        {/* Component JSX */}
      </>
    )
    

    这将阻止任何路由,但不会阻止页面刷新或关闭。要阻止这种情况,您需要添加以下内容(根据需要使用适当的React生命周期进行更新):

    componentDidUpdate = () => {
      if (shouldBlockNavigation) {
        window.onbeforeunload = () => true
      } else {
        window.onbeforeunload = undefined
      }
    }
    

    onbeforeunload 有多种浏览器支持。

        2
  •  60
  •   Vebjorn Ljosa    5 年前

    In-react路由器 v2.4.0 或以上 v4 有几种选择

    1. Add function onLeave for Route
     <Route
          path="/home"
          onEnter={ auth }
          onLeave={ showConfirm }
          component={ Home }
        >
        
    
    1. Use function setRouteLeaveHook for componentDidMount

    您可以防止发生转换,或在使用离开挂钩离开路线之前提示用户。

    const Home = withRouter(
      React.createClass({
    
        componentDidMount() {
          this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
        },
    
        routerWillLeave(nextLocation) {
          // return false to prevent a transition w/o prompting the user,
          // or return a string to allow the user to decide:
          // return `null` or nothing to let other hooks to be executed
          //
          // NOTE: if you return true, other hooks will not be executed!
          if (!this.state.isSaved)
            return 'Your work is not saved! Are you sure you want to leave?'
        },
    
        // ...
    
      })
    )
    

    请注意,此示例使用 withRouter 中引入的高阶组件 v2.4.0.

    然而,当手动更改URL中的路由时,这些解决方案并不能很好地工作

    在这个意义上

    • 我们看到确认-ok
    • 页面的包含未重新加载-确定
    • URL未更改-不正常

    对于 react-router v4 使用提示或自定义历史记录:

    然而,在 react路由器v4 ,在 Prompt 来自'react-router

    根据文件

    促使

    用于在离开页面之前提示用户。当您的 应用程序进入的状态应阻止用户 导航离开(就像一个表单填写了一半),渲染 <Prompt> 。

    import { Prompt } from 'react-router'
    
    <Prompt
      when={formIsHalfFilledOut}
      message="Are you sure you want to leave?"
    />
    

    消息:字符串

    当用户尝试导航离开时提示用户的消息。

    <Prompt message="Are you sure you want to leave?"/>
    

    消息:func

    将调用用户的下一个位置和操作 正在尝试导航到。返回字符串以向 user或true以允许转换。

    <Prompt message={location => (
      `Are you sure you want to go to ${location.pathname}?`
    )}/>
    

    时间:布尔

    而不是有条件地呈现 <提示(>); 在守卫后面,你 始终可以渲染它,但通过 when={true} 或 when={false} 到 相应地阻止或允许导航。

    在渲染方法中,只需根据需要添加文档中提到的内容即可。

    更新时间:

    如果您想在用户离开页面时执行自定义操作,您可以使用自定义历史记录并配置路由器,如

    历史js公司

    import createBrowserHistory from 'history/createBrowserHistory'
    export const history = createBrowserHistory()
    
    ... 
    import { history } from 'path/to/history';
    <Router history={history}>
      <App/>
    </Router>
    

    然后在您的组件中,您可以利用 history.block 喜欢

    import { history } from 'path/to/history';
    class MyComponent extends React.Component {
       componentDidMount() {
          this.unblock = history.block(targetLocation => {
               // take your action here     
               return false;
          });
       }
       componentWillUnmount() {
          this.unblock();
       }
       render() {
          //component render here
       }
    }
    
        3
  •  22
  •   psiyumm    10 年前

    对于 react-router 2.4.0+

    注意事项 :建议将所有代码迁移到最新版本 react-router 去买所有的新东西。

    按照 react-router documentation :

    应使用 withRouter 高阶组件:

    我们认为这个新的HoC更好、更简单,并将在 文档和示例,但不难要求 转换

    作为文档中的ES6示例:

    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 <div>Stuff</div>
      }
    
    })
    
    export default withRouter(Page)
    
        4
  •  9
  •   Barry Staes    8 年前

    对于 react-router v3.x版

    我也有同样的问题,我需要一条确认消息来确认页面上任何未保存的更改。在我的情况下,我使用 React路由器v3 ,所以我无法使用 <Prompt /> ,它是从 React路由器v4 。

    我处理了“后退按钮点击”和“意外链接点击”的组合 setRouteLeaveHook 和 history.pushState() ,并使用 onbeforeunload 事件处理程序。

    setRouteLeaveHook ( doc )& 历史pushState公司 ( doc )

    • 仅使用setRouteLeaveHook是不够的。由于某种原因,URL已更改,尽管单击“后退按钮”时页面保持不变。

        // setRouteLeaveHook returns the unregister method
        this.unregisterRouteHook = this.props.router.setRouteLeaveHook(
          this.props.route,
          this.routerWillLeave
        );
      
        ...
      
        routerWillLeave = nextLocation => {
          // Using native 'confirm' method to show confirmation message
          const result = confirm('Unsaved work will be lost');
          if (result) {
            // navigation confirmed
            return true;
          } else {
            // navigation canceled, pushing the previous path
            window.history.pushState(null, null, this.props.route.path);
            return false;
          }
        };
      

    卸载前打开 ( doc )

    • 用于处理“意外重新加载”按钮

      window.onbeforeunload = this.handleOnBeforeUnload;
      
      ...
      
      handleOnBeforeUnload = e => {
        const message = 'Are you sure?';
        e.returnValue = message;
        return message;
      }
      

    下面是我写的完整组件

    • 请注意 withRouter 过去有 this.props.router 。
    • 请注意 this.props.route 从调用组件向下传递
    • 请注意 currentState 作为道具传递以具有初始状态并检查任何更改

      import React from 'react';
      import PropTypes from 'prop-types';
      import _ from 'lodash';
      import { withRouter } from 'react-router';
      import Component from '../Component';
      import styles from './PreventRouteChange.css';
      
      class PreventRouteChange extends Component {
        constructor(props) {
          super(props);
          this.state = {
            // initialize the initial state to check any change
            initialState: _.cloneDeep(props.currentState),
            hookMounted: false
          };
        }
      
        componentDidUpdate() {
      
         // I used the library called 'lodash'
         // but you can use your own way to check any unsaved changed
          const unsaved = !_.isEqual(
            this.state.initialState,
            this.props.currentState
          );
      
          if (!unsaved && this.state.hookMounted) {
            // unregister hooks
            this.setState({ hookMounted: false });
            this.unregisterRouteHook();
            window.onbeforeunload = null;
          } else if (unsaved && !this.state.hookMounted) {
            // register hooks
            this.setState({ hookMounted: true });
            this.unregisterRouteHook = this.props.router.setRouteLeaveHook(
              this.props.route,
              this.routerWillLeave
            );
            window.onbeforeunload = this.handleOnBeforeUnload;
          }
        }
      
        componentWillUnmount() {
          // unregister onbeforeunload event handler
          window.onbeforeunload = null;
        }
      
        handleOnBeforeUnload = e => {
          const message = 'Are you sure?';
          e.returnValue = message;
          return message;
        };
      
        routerWillLeave = nextLocation => {
          const result = confirm('Unsaved work will be lost');
          if (result) {
            return true;
          } else {
            window.history.pushState(null, null, this.props.route.path);
            if (this.formStartEle) {
              this.moveTo.move(this.formStartEle);
            }
            return false;
          }
        };
      
        render() {
          return (
            <div>
              {this.props.children}
            </div>
          );
        }
      }
      
      PreventRouteChange.propTypes = propTypes;
      
      export default withRouter(PreventRouteChange);
      

    如果有任何问题,请告诉我:)

        5
  •  3
  •   ravibagul91    7 年前

    使用历史记录。听

    例如,如下所示:

    在您的组件中,

    componentWillMount() {
        this.props.history.listen(() => {
          // Detecting, user has changed URL
          console.info(this.props.history.location.pathname);
        });
    }
    
        6
  •  3
  •   Eugene Charniauski    4 年前

    这就是当用户切换到其他路由或离开当前页面并转到其他URL时显示消息的方式

    import PropTypes from 'prop-types'
    import React, { useEffect } from 'react'
    import { Prompt } from 'react-router-dom'
    import { useTranslation } from 'react-i18next'
    
    
    const LeavePageBlocker = ({ when }) => {
      const { t } = useTranslation()
      const message = t('page_has_unsaved_changes')
    
      useEffect(() => {
        if (!when) return () => {}
    
        const beforeUnloadCallback = (event) => {
          event.preventDefault()
          event.returnValue = message
          return message
        }
    
        window.addEventListener('beforeunload', beforeUnloadCallback)
        return () => {
          window.removeEventListener('beforeunload', beforeUnloadCallback)
        }
      }, [when, message])
    
      return <Prompt when={when} message={message} />
    }
    
    LeavePageBlocker.propTypes = {
      when: PropTypes.bool.isRequired,
    }
    
    export default LeavePageBlocker
    

    您的页面:

    const [dirty, setDirty] = setState(false)
    ...
    return (
      <>
        <LeavePageBlocker when={dirty} />
        ...
      </>
    )
    
        7
  •  2
  •   Barry Staes    10 年前

    对于 react-router v0.13。x带 react v0.13。x:

    这可以通过 willTransitionTo() 和 willTransitionFrom() 静态方法。有关更新版本,请参阅下面我的其他答案。

    从 react-router documentation :

    您可以在路由处理程序上定义一些静态方法,这些方法将在路由转换期间调用。

    willTransitionTo(transition, params, query, callback)

    当处理程序即将呈现时调用,使您有机会中止或重定向转换。您可以在执行一些异步工作时暂停转换,并在完成后调用回调(错误),或者在参数列表中省略回调,它将被调用。

    willTransitionFrom(transition, component, callback)

    当活动路由正在向外转换时调用,使您有机会中止转换。该组件是当前组件,您可能需要它检查其状态以决定是否允许转换(如表单字段)。

    实例

      var Settings = React.createClass({
        statics: {
          willTransitionTo: function (transition, params, query, callback) {
            auth.isLoggedIn((isLoggedIn) => {
              transition.abort();
              callback();
            });
          },
    
          willTransitionFrom: function (transition, component) {
            if (component.formHasUnsavedData()) {
              if (!confirm('You have unsaved information,'+
                           'are you sure you want to leave this page?')) {
                transition.abort();
              }
            }
          }
        }
    
        //...
      });
    

    对于 react路由器 1.0.0-rc1带 反应 v0.14。x或更高版本:

    这应该可以通过 routerWillLeave 生命周期挂钩。对于旧版本,请参见上面的我的答案。

    从 react-router documentation :

    要安装此挂钩,请在其中一个route组件中使用Lifecycle mixin。

      import { Lifecycle } from 'react-router'
    
      const Home = React.createClass({
    
        // Assuming Home is a route component, it may use the
        // Lifecycle mixin to get a routerWillLeave method.
        mixins: [ Lifecycle ],
    
        routerWillLeave(nextLocation) {
          if (!this.state.isSaved)
            return 'Your work is not saved! Are you sure you want to leave?'
        },
    
        // ...
    
      })
    

    事情。可能会在最终版本之前更改。

        8
  •  2
  •   Jaskaran Singh    7 年前

    您可以使用此提示。

    import React, { Component } from "react";
    import { BrowserRouter as Router, Route, Link, Prompt } from "react-router-dom";
    
    function PreventingTransitionsExample() {
      return (
        <Router>
          <div>
            <ul>
              <li>
                <Link to="/">Form</Link>
              </li>
              <li>
                <Link to="/one">One</Link>
              </li>
              <li>
                <Link to="/two">Two</Link>
              </li>
            </ul>
            <Route path="/" exact component={Form} />
            <Route path="/one" render={() => <h3>One</h3>} />
            <Route path="/two" render={() => <h3>Two</h3>} />
          </div>
        </Router>
      );
    }
    
    class Form extends Component {
      state = { isBlocking: false };
    
      render() {
        let { isBlocking } = this.state;
    
        return (
          <form
            onSubmit={event => {
              event.preventDefault();
              event.target.reset();
              this.setState({
                isBlocking: false
              });
            }}
          >
            <Prompt
              when={isBlocking}
              message={location =>
                `Are you sure you want to go to ${location.pathname}`
              }
            />
    
            <p>
              Blocking?{" "}
              {isBlocking ? "Yes, click a link or the back button" : "Nope"}
            </p>
    
            <p>
              <input
                size="50"
                placeholder="type something to block transitions"
                onChange={event => {
                  this.setState({
                    isBlocking: event.target.value.length > 0
                  });
                }}
              />
            </p>
    
            <p>
              <button>Submit to stop blocking</button>
            </p>
          </form>
        );
      }
    }
    
    export default PreventingTransitionsExample;
    
        9
  •  0
  •   quantum.snowball    5 年前

    也许你可以用 componentWillUnmount() 在用户离开页面之前执行任何操作。如果您使用的是功能组件,那么您可以对 useEffect() 钩钩子接受一个函数,该函数返回 Destructor ,这与 组件将卸载() 可以这样做。

    贷记至 this article