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

}