react-router v4:防止使用自定义挂钩而不是提示进行转换
react-router v4: prevent transition with custom hook instead of Prompt
在 react-router
v3 中,我一直在使用 router.setRouteLeaveHook
检查表单是否有未保存的更改,如果有,return false
会阻止转换。然后我会显示一个带有 3 个按钮的自定义 bootstrap 模式对话框:保存更改、放弃更改和留在此处。
我无法使用 react-router
v4 的 Prompt
组件来执行此操作,因为无法自定义浏览器确认对话框中显示的按钮。似乎他们放弃了取消过渡的任何方式,只允许您要求用户在浏览器确认对话框中批准过渡。
我尝试查看 Prompt
的代码,但 it just passes the dialog message to history
,所以我不知道如何设置 v3 样式的路由离开钩子。
甚至还有可能吗?还是 react-router
开发人员出于某种原因故意决定删除此功能?
我认为这是不可能的。 react-router-v4 使用一个名为 history 的包,该包又使用 HTML5 历史记录 api。历史记录 api 仅在您点击后退按钮 (onpopstate) 时通知您,如果您考虑一下这很有意义,因为您不想赋予网站不允许您在页面之间移动的权力。
你能做的最好的事情是 window onbeforeunload event,它会创建一个提示,要求用户确认,但这正是 react-router 公开给你使用的内容。
你可能会通过 monkey-patching react-router 的内部 history 对象获得你想要的一些功能,这样你就可以添加你自己的行为。但有一点需要注意,这只会在你 react-router 的 <Link />
组件和朋友时起作用,所以你将无法拦截刷新和其他你可能想要的东西。
如果您想走那条路,请告诉我,我可以为您提供一些见解或代码示例,说明它是如何工作的,我会更新我的答案。
根据 the history package docs,您可以将 window.confirm
替换为您喜欢的任何内容:
By default, window.confirm is used to show prompt messages to the user. If you need to override this behavior (or if you're using createMemoryHistory, which doesn't assume a DOM environment), provide a getUserConfirmation function when you create your history object.
因此,如果您想使用自己的对话框,应该可以通过以下方式完成:
const history = createHistory({
getUserConfirmation(message, callback) {
showMyCustomDialog(message)
.then(result => callback(result === 'The YES button'))
}
})
这意味着您设置的任何 getUserConfirmation
消息都是为整个 session 设置的,但您可以将其抽象到包含对话的其他详细信息的导航阻止层,例如标题、按钮文本、颜色等
或者,您可以劫持 message
参数并将其用于对话框配置,尽管这可能闻起来有点难闻。但这不是一个完美的系统,因此您所做的任何事情都可能有点乱七八糟。
React Router v4 允许您在创建路由器时通过此方法(参见 here):
<BrowserRouter getUserConfirmation={yourConfirmationFunction} />
可以使用Prompt
显示自定义对话。信用和详细解释 here.
Prompt
需要消息道具,这里我们可以使用自定义函数进行对话,它应该 return false
以防止导航。
const BlockingPage = () => {
const [block, setBlock] = useState(true);
const blockedNavigation = (nLocation) => {
//nLocation gives the next location object
/**
* Your custom logic
*
**/
//required to block navigation
return false
}
return(
<div>
<Prompt when={block} message={blockedNavigation}/>
</div>
)
}
在 react-router
v3 中,我一直在使用 router.setRouteLeaveHook
检查表单是否有未保存的更改,如果有,return false
会阻止转换。然后我会显示一个带有 3 个按钮的自定义 bootstrap 模式对话框:保存更改、放弃更改和留在此处。
我无法使用 react-router
v4 的 Prompt
组件来执行此操作,因为无法自定义浏览器确认对话框中显示的按钮。似乎他们放弃了取消过渡的任何方式,只允许您要求用户在浏览器确认对话框中批准过渡。
我尝试查看 Prompt
的代码,但 it just passes the dialog message to history
,所以我不知道如何设置 v3 样式的路由离开钩子。
甚至还有可能吗?还是 react-router
开发人员出于某种原因故意决定删除此功能?
我认为这是不可能的。 react-router-v4 使用一个名为 history 的包,该包又使用 HTML5 历史记录 api。历史记录 api 仅在您点击后退按钮 (onpopstate) 时通知您,如果您考虑一下这很有意义,因为您不想赋予网站不允许您在页面之间移动的权力。
你能做的最好的事情是 window onbeforeunload event,它会创建一个提示,要求用户确认,但这正是 react-router 公开给你使用的内容。
你可能会通过 monkey-patching react-router 的内部 history 对象获得你想要的一些功能,这样你就可以添加你自己的行为。但有一点需要注意,这只会在你 react-router 的 <Link />
组件和朋友时起作用,所以你将无法拦截刷新和其他你可能想要的东西。
如果您想走那条路,请告诉我,我可以为您提供一些见解或代码示例,说明它是如何工作的,我会更新我的答案。
根据 the history package docs,您可以将 window.confirm
替换为您喜欢的任何内容:
By default, window.confirm is used to show prompt messages to the user. If you need to override this behavior (or if you're using createMemoryHistory, which doesn't assume a DOM environment), provide a getUserConfirmation function when you create your history object.
因此,如果您想使用自己的对话框,应该可以通过以下方式完成:
const history = createHistory({
getUserConfirmation(message, callback) {
showMyCustomDialog(message)
.then(result => callback(result === 'The YES button'))
}
})
这意味着您设置的任何 getUserConfirmation
消息都是为整个 session 设置的,但您可以将其抽象到包含对话的其他详细信息的导航阻止层,例如标题、按钮文本、颜色等
或者,您可以劫持 message
参数并将其用于对话框配置,尽管这可能闻起来有点难闻。但这不是一个完美的系统,因此您所做的任何事情都可能有点乱七八糟。
React Router v4 允许您在创建路由器时通过此方法(参见 here):
<BrowserRouter getUserConfirmation={yourConfirmationFunction} />
可以使用Prompt
显示自定义对话。信用和详细解释 here.
Prompt
需要消息道具,这里我们可以使用自定义函数进行对话,它应该 return false
以防止导航。
const BlockingPage = () => {
const [block, setBlock] = useState(true);
const blockedNavigation = (nLocation) => {
//nLocation gives the next location object
/**
* Your custom logic
*
**/
//required to block navigation
return false
}
return(
<div>
<Prompt when={block} message={blockedNavigation}/>
</div>
)
}