如何使用react-router <Prompt>显示组件以防止或允许更改路由

时间:2019-06-19 09:39:34

标签: javascript reactjs react-router react-router-v4 react-router-dom

我目前正在尝试找到一种显示自定义组件(如Modal)的方法,以使用Prompt组件来确认路线更改。

Promp组件的默认行为是显示带有消息的确认对话框,如您在此Example: React Router: Preventing Transitions.中所见

注意:我正在使用<BrowserRouter>组件。

路由器具有一个名为prop的{​​{1}},可用于自定义getUserConfirmation组件的行为。

<Prompt>

我要做什么:

  • 在父组件APP内
    • 我将// this is the default behavior function getConfirmation(message, callback) { const allowTransition = window.confirm(message); callback(allowTransition); } <BrowserRouter getUserConfirmation={getConfirmation} />; 状态设置为true,以显示confirm组件
    • 我正在尝试将<Confirm>callback函数传递到getConfirmation组件,以使用<Confirm>进行转换,并使用{{1 }}来阻止它。
    • 如上所示,将使用默认行为true调用回调。
false

这是 App.js 呈现的内容:

true or false

问题出在哪里:

  • function getConfirmation(message, callback) { console.log("Inside getConfirmation function..."); setConfirmCallback(callback); setConfirm(true); // const allowTransition = window.confirm(message); // callback(allowTransition); } 对话框似乎在那时阻止了该功能。因此return ( <Router getUserConfirmation={getConfirmation}> <AllRoutes /> {confirm && ( <Confirm confirmCallback={confirmCallback} setConfirm={setConfirm} /> )} </Router> ); 变量/参数仍在范围内。这样一切正常。
  • 当我删除confirm对话框时,该函数将一直运行。而且,当我单击callback组件内的确认按钮时,confirm不再存在。

问题

有人知道使用<Confirm>来实现此行为的方法(使用自定义组件而不是确认对话框来防止路由更改)吗?

Link to CodeSandbox

来自CodeSandbox的完整代码:

callback

2 个答案:

答案 0 :(得分:0)

受此discussion和此example的启发,我能够使我的示例正常工作。

问题在于创建<Confirm>时,setConfirmCallback()调用尚未完成。因此<Confirm>组件无法使用callback中的getUserConfirmation

所以我更改了这一行:

FROM:
  setConfirmCallback(callback);
TO:
  setConfirmCallback(()=>callback);

现在可以使用了!

CodeSandbox Link

完整代码沙盒代码:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Link,
  Prompt
} from "react-router-dom";

import "./styles.css";

function App() {
  console.log("Rendering App...");

  const [confirm, setConfirm] = useState(false);
  const [confirmCallback, setConfirmCallback] = useState(null);

  function getConfirmation(message, callback) {
    console.log("Inside getConfirmation function...");
    setConfirmCallback(() => callback);
    setConfirm(true);
    // const allowTransition = window.confirm(message);
    // callback(allowTransition);
  }

  return (
    <Router getUserConfirmation={getConfirmation}>
      <AllRoutes />
      {confirm && (
        <Confirm confirmCallback={confirmCallback} setConfirm={setConfirm} />
      )}
    </Router>
  );
}

function Confirm(props) {
  console.log("Rendering Confirm...");
  function allowTransition() {
    props.setConfirm(false);
    props.confirmCallback(true);
  }

  function blockTransition() {
    props.setConfirm(false);
    props.confirmCallback(false);
  }

  return (
    <React.Fragment>
      <div>Are you sure?</div>
      <button onClick={allowTransition}>Yes</button>
      <button onClick={blockTransition}>No way</button>
    </React.Fragment>
  );
}

function AllRoutes(props) {
  console.log("Rendering AllRoutes...");
  return (
    <Switch>
      <Route exact path="/" component={Home} />
      <Route exact path="/comp1" component={Component1} />
    </Switch>
  );
}

function Home(props) {
  console.log("Rendering Home...");
  return (
    <React.Fragment>
      <div>This is Home</div>
      <ul>
        <li>
          <Link to="/comp1">Component1</Link>
        </li>
      </ul>
    </React.Fragment>
  );
}

function Component1(props) {
  console.log("Rendering Component1...");

  const [isBlocking, setIsBlocking] = useState(true);

  return (
    <React.Fragment>
      <Prompt
        when={isBlocking}
        message={location =>
          `Are you sure you want to go to ${location.pathname}`
        }
      />
      <div>This is component 1</div>
      <Link to="/">Home</Link>
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

答案 1 :(得分:0)

我为我的案例找到了一个简单的解决方法。我无法分享整个组件,只能分享片段。

function App() {
  const [showForm, setShowForm] = useState(true);
  const [tasks, setTasks] = useState([]);
  const [isSubmitted, setIsSubmitted] = useState(false);

  //Fetch tasks from server
  const fetchData = () => {
    fetch(
      "https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json"
    )
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        const tasks = [];
        //Convert the data to an array so i can map over it
        for (const key in data) {
          const task = {
            id: key,
            ...data[key],
          };

          tasks.push(task);
        }

        setTasks(tasks);
      });
  };
  useEffect(() => {
    if (isSubmitted) {
       fetchData();
       setIsSubmitted(false);
    }
  }, [isSubmitted]);

  //Show/Hide form
  const onAddHandler = () => {
    setShowForm(!showForm);
  };

  const formSubmitted = () => {
    setIsSubmitted(true);
    console.log(isSubmitted);
  };
  return (
    <Container>
      <Header click={onAddHandler} isShown={showForm}></Header>
      {showForm ? <Form fs={formSubmitted}></Form> : ""}
      <Tasks tasks={tasks}></Tasks>
    </Container>
  );
}

export default App;