简体   繁体   English

如何在 react-router-dom 中实现像 vue-router 这样的路由器离开守卫?

[英]How to implement router leave guard like vue-router in react-router-dom?

I need to ask user if submit form or not in react project like in vue-router beforeRouteLeave guard:我需要询问用户是否在反应项目中提交表单,例如在 vue-router beforeRouteLeave 守卫中:

<script>
export default {
  name: 'RolePage',
  methods: {
    http() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve({ ok: true })
        }, 200)
      })
    },
  },
  async beforeRouteLeave(to, from, next) {
    const answer = window.confirm('Do you want submit form?')
    answer && (await this.http())
    next()
  },
}
</script>

This work well when user click goback and forward button in browser menu and Programmatic Navigation work as well.当用户单击浏览器菜单中的返回和前进按钮时,这会很好地工作,并且程序化导航也能正常工作。 How can I implement the same requirement in react-router-dom?如何在 react-router-dom 中实现相同的要求?

I believe the answer is to use a blocking flag.我相信答案是使用阻塞标志。 See this: https://reactrouter.com/web/example/preventing-transitions请参阅: https : //reactrouter.com/web/example/preventing-transitions

Either you can create this using react-router APIs or use an already available package likeReact Navigation Prompt .您可以使用 react-router API 创建它,也可以使用已经可用的包,如React Navigation Prompt

It uses react-router under the hood它在引擎盖下使用 react-router

It's the simplest example to prevent a user to change the route by using the react-router and react-router-dom packages.这是通过使用react-routerreact-router-dom包来防止用户更改路由的最简单示例。

import React from "react";
import { BrowserRouter, Route, NavLink } from "react-router-dom";
import { Prompt } from "react-router";
import "./App.css";

function App() {
  const [dirty, setDirty] = React.useState(false);
  return (
    <BrowserRouter>
      <div className="App">
       <input onChange={() => setDirty(true)} /> {dirty ? "dirty" : "clean"}
       <br />
       <NavLink to="/test">Leave</NavLink>
       <br />
       <Route path="/test" component={() => "Has left"} />
       <Prompt message="Are you sure you want to go to /test?" />
     </div>
    </BrowserRouter>
  );
}
export default App;

You just need to install these two packages react-router and react-router-dom .你只需要安装这两个包react-routerreact-router-dom This is the simplest logic which is built like if your form is dirty then you should prompt the user and prevent them from the leaving the page.这是最简单的逻辑,就像您的表单脏了一样,您应该提示用户并防止他们离开页面。

You could do the same in React using window.confirm .你可以在 React 中使用window.confirm做同样的事情。 Doing it the react way by using state value will have one problem;state值来做 react 会有一个问题; timing the 2 async actions: setState and your async function call based on the value of answer .根据answer的值对 2 个异步操作计时setState和异步函数调用。

Example below:下面的例子:

App.js应用程序.js

import "./styles.css";
import { Switch, Route } from "react-router-dom";
import Test from "./Test";
import Leave from "./Leaving";

export default function App() {
  return (
    <div className="App">
      <Switch>
        <Route exact path="/prompt" component={Leave} />
      </Switch>
      <Test />
    </div>
  );
}

Test.js测试.js

import React, { useState } from "react";
import { Prompt, useHistory } from "react-router-dom";

function Test() {

  const [answer, setIsAnswer] = useState(false);

  const history = useHistory();

  const http = () => {
    console.log("I'll show up on submit");
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({ ok: true });
      }, 200);
    });
  };


  const handleSubmit = async (e) => {
    console.log("**********");
    e.preventDefault();
    e.target.reset();
    setIsAnswer(true);  // setting state is async
    history.push("/prompt");
    answer && (await http()); // answer is yet false
  };

  return (
    <div className="Test">
      <h1>Hello Jack</h1>
      <form onSubmit={(e) => handleSubmit(e)}>
          <Prompt when={true} message={(_) => "Do you want submit form?"} /> // **when**'s value should ideally depend on answer value, harcoded for example purpose.
        <button>Click</button>
      </form>
    </div>
  );
}

export default Test;

Only when you click the second time, the React way of prompt is in action since setIsAnswer is async.只有当你第二次点击时,React 的提示方式setIsAnswer ,因为setIsAnswer是异步的。

You could use the same window.confirm onSubmit.您可以使用相同的window.confirm onSubmit。

const handleSubmit = async (e) => {
    console.log("**********");
    e.preventDefault();
    e.target.reset();
    const answer = window.confirm("Do you want submit form?");
    answer && history.push("/prompt");
    answer && (await http());
  };
    
     

I created a working example for you here in codesandbox https://codesandbox.io/s/charming-dew-u32wx我在 codeandbox https://codesandbox.io/s/charming-dew-u32wx 中为您创建了一个工作示例

In the above sandbox, handleSubmit is plain JS way.在上面的沙箱中, handleSubmit是普通的 JS 方式。 handleSubmit2 is React way(click twice). handleSubmit2是 React 方式(单击两次)。 Remove the commented <Prompt .../> inside the form and change onSubmit function to handleSubmit then handleSubmit2 to see both in action.删除表单内的注释<Prompt .../>并将onSubmit函数更改为 handleSubmit 然后 handleSubmit2 以查看两者的运行情况。

Explanation :说明

Any react module or approach that will rely on the state value will not help with your scenario, where you make HTTP call after prompt confirmation.任何依赖状态值的反应模块或方法都不会帮助您在提示确认后进行 HTTP 调用的场景。 window.comfirm is blocking code, using state for answer is non-blocking. window.comfirm是阻塞代码,使用状态作为answer是非阻塞的。 If it's just about prompting before navigating away then you could go with the above react or any other react approach suggested.如果只是在导航之前进行提示,那么您可以使用上述反应或建议的任何其他反应方法。

Update in response to comments:根据评论更新:

You could prevent the browser back button navigation by adding the below code.您可以通过添加以下代码来阻止浏览器后退按钮导航。

const history = useHistory();

  useEffect(() => {
    return () => {
      if (history.action === "POP") {
       return false;
      }
   };
 }, [history]);

or in your Prompt itself through callback通过回调在您的提示本身中

<Prompt
    when={(location, action) => {
      if (action === "POP") {
        //
      }
    }}
    message={(_) => "Are you sure, you want to go back?"}
/>

I've updated the Leaving.js in the codesandbox with this browser back button scenario.我已经使用此浏览器后退按钮方案更新了代码和框中的Leaving.js

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM