Как отобразить компонент с помощью response-router <Prompt>, чтобы предотвратить или разрешить изменение маршрута - PullRequest
0 голосов
/ 19 июня 2019

В настоящее время я пытаюсь найти способ отображения пользовательского компонента (например, модального) для подтверждения изменений маршрута с использованием компонента Prompt.

Поведение компонента Promp по умолчанию заключается впоказать диалоговое окно подтверждения с сообщением, как вы можете видеть в этом Пример: React Router: Предотвращение переходов.

Примечание: я использую компонент <BrowserRouter>.

Маршрутизатор имеет prop с именем getUserConfirmation, который можно использовать для настройки поведения компонента <Prompt>.

// this is the default behavior

function getConfirmation(message, callback) {
  const allowTransition = window.confirm(message);
  callback(allowTransition);
}

<BrowserRouter getUserConfirmation={getConfirmation} />;

Что я пытаюсь сделать:

  • Внутри родительского компонента APP
    • Я устанавливаю состояние confirm в true, чтобы отобразить компонент <Confirm>
    • И япопытка передать callback из функции getConfirmation компоненту <Confirm> для вызова его с помощью true для разрешения перехода и с false для его предотвращения.
    • Будет вызван обратный вызовс true or false в поведении по умолчанию, как вы можете видеть выше.
function getConfirmation(message, callback) {
    console.log("Inside getConfirmation function...");
    setConfirmCallback(callback);
    setConfirm(true);
    // const allowTransition = window.confirm(message);
    // callback(allowTransition);
  }

Вот что App.js выполняет рендеринг:

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

В чем проблема:

  • Похоже, диалоговое окно confirmзаблокировать функцию в этой точке.Таким образом, переменная / параметр callback все еще находится в области видимости.Так что все работает хорошо.
  • Когда я удаляю диалог confirm, эта функция запускается полностью.И когда я нажимаю кнопку подтверждения внутри компонента <Confirm>, callback больше не существует.

ВОПРОС

Кто-нибудь знает способчтобы добиться такого поведения (предотвращение изменения маршрута с помощью пользовательского компонента вместо диалогового окна подтверждения) с помощью react-router-dom?

Ссылка на CodeSandbox

Полный код из CodeSandbox:

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) {
  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 голосов
/ 19 июня 2019

Вдохновленный этим обсуждением и этим примером , я смог заставить свой пример работать.

Проблема заключалась в том, что при создании <Confirm> вызов setConfirmCallback() еще не был сделан. Поэтому компонент <Confirm> не смог использовать callback из getUserConfirmation.

Итак, я изменил эту строку:

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

И теперь это работает!

CodeSandbox Link

Полный код CodeSandbox:

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);

...