javascript - React Router v5.2 - 使用 createBrowserHistory 和 history.block 阻止路由更改
问题描述
我的应用程序有两个页面:Step1
和Step2
. Step1
有一个复选框,如果选中则阻止导航,还有一个Step2
单击时导航到的下一步按钮。Step2
有一个“上一个”按钮,单击该按钮可返回到该Step1
按钮。
根据本教程,如果选中了复选框,我将使用对象的block
方法createBrowserHistory
来阻止路由更改Step1
:
const unblock = useRef();
useEffect(() => {
unblock.current = history.block((tx) => {
if (block.current.checked) {
const promptMessage = "Are you sure you want to leave?";
if (window.confirm(promptMessage)) {
unblock.current();
tx.retry();
}
} else {
console.log("tfgx");
unblock.current();
tx.retry();
}
});
}, []);
我还必须将低级<Router>
(不是<BrowserRouter>
)中的历史道具设置为createBrowserHistory
对象,如下所示:
<Router history={createBrowserHistory()}>
...
</Router>
但这会阻止路线被正确渲染。我认为这可能与<Switch>
无法正确读取位置对象有关。如果我使用<BrowserRouter>
,则位置对象如下所示{pathname: "/step1", ... key: "7sd45"}
:但是当我使用时<Router={createBrowserHistory()}>
,位置对象看起来像这样{action: "PUSH", location: {pathname: "/step1", ... key: "7sd45"}}
。(我也收到了警告You cannot change <Router history>
。)
如果选中“阻止导航”复选框,我想要的结果是阻止导航,否则取消阻止。如果导航畅通时位置发生变化,我希望正确呈现相应的路线。
React Router v5 文档中关于 createBrowserHisory 的部分很少,并且没有很多使用它的示例,所以如果有人能对此有所了解,我将不胜感激。
编辑:传递location.location
给<Switch>
似乎可以解决它(更新的演示)。但是,如果我useLocation
在内部调用Step1
并打印结果(第 17-18 行),我会得到{pathname: "/step1", ... key: "7sd45"}
而不是{action: "PUSH", location: {pathname: "/step1", ... key: "7sd45"}}
. 为什么是这样?
此外,如果用户在导航被阻止时尝试去另一个位置,我的自定义提示会按预期显示(“您确定要离开”,带有“确定”和“取消”按钮)。但是,如果他们通过单击取消取消此操作,则会出现浏览器自己的对话框 -
在 Chrome 中:
在火狐中:
在我的提示被解除后是否可以禁止浏览器提示?
解决方案
路由器上下文的history
对象也有一个块功能,但它的工作方式略有不同。它需要一个消耗location
和action
参数的回调。
history.block((location, action) => {...});
从回调返回false
阻止导航转换,返回true
允许转换通过。
React.useEffect(() => {
const unblock = history.block((location, action) => {
if (checkBlockingCondition) {
return window.confirm("Navigate Back?");
}
return true;
});
return () => {
unblock();
};
}, []);
或者,react-router-dom 建议使用该Prompt
组件有条件地阻止路由转换。您的代码非常接近他们的防止转换示例。
更新您的最后一个代码框:
- 使用阻塞状态与反应参考,以便提示重新呈现并重新评估条件。
- 渲染一个
Prompt
组件。 - 阻止默认的表单提交动作,即阻止页面重新加载。
代码
import {
BrowserRouter as Router,
Prompt, // <-- import Prompt
Redirect,
Switch,
Route,
useLocation
} from "react-router-dom";
const Step1 = ({ id, history }) => {
const [isBlocking, setIsBlocking] = useState(false);
return (
<form
id={id}
onSubmit={(e) => {
e.preventDefault(); // <-- prevent default form action, i.e. page reload
history.push("/step2");
}}
>
<label>
Block navigation
<input
type="checkbox"
onChange={(e) => setIsBlocking(e.target.checked)}
/>
</label>
<br />
<br />
<button type="submit">Next</button>
<Prompt
when={isBlocking} // <-- blocking condition
message="Are you sure you want to leave?"
/>
</form>
);
};
推荐阅读
- python - 掩码 tf keras 中分段分割的损失函数
- python - 有没有办法在这个简单的 for 循环中解释标点符号
- bash - 在匹配另一行之后删除匹配的行
- acumatica - Override a Dataview in acumatica
- node.js - Finding and Updating record - Mongoose
- mongodb - 返回分组数据中的最大数字
- java - 有没有办法在 RecyclerView 下添加按钮或任何东西
- ssl - Kubernetes 和 externalIP 中 TLS RabbitMQ 的奇怪行为
- javascript - Get tests running time with Jest
- cypress - 在 Cypress 中切换到另一个窗口