导航到其他页面时获取 navigator.block 不是一个函数

Pra*_*til 8 javascript reactjs react-router-dom

在 React 项目中,我创建了一个弹出模式,当任何用户尝试在输入字段中进行任何更改并导航到其他屏幕时,都会显示该模式。它没有按预期工作,因此浏览了很多帖子来找到解决方案,但没有运气。请参考下面的代码:

useBlock.js

import {useContext, useEffect} from 'react';
import { UNSAFE_NavigationContext as NavigationContext} from 'react-router-dom';
const useBlocker = (blocker, when = true) => {
    const navigator = useContext(NavigationContext).navigator
    useEffect(() => {
        if (!when)
            return;
        const unblock = navigator.block((tx) => { <-- This line is creating an issue
            const autoUnblockingTx = {
                ...tx,
                retry() {
                  unblock();
                  tx.retry();
                },
              };
            blocker(autoUnblockingTx);
        });
        return unblock;
    }, [navigator, blocker, when]);
}

export default useBlocker
Run Code Online (Sandbox Code Playgroud)

useCallbackPrompt.js

import { useCallback, useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router';
import useBlocker from './useBlocker';

const useCallbackPrompt = (when) => {
    const navigate = useNavigate();
    const location = useLocation();
    const [showPrompt, setShowPrompt] = useState(false);
    const [lastLocation, setLastLocation] = useState(null);
    const [confirmedNavigation, setConfirmedNavigation] = useState(false);
    const cancelNavigation = useCallback(() => {
        setShowPrompt(false);
    }, []);

    const handleBlockedNavigation = useCallback((nextLocation) => {
        if (!confirmedNavigation &&
            nextLocation.location.pathname !== location.pathname) {
            setShowPrompt(true);
            setLastLocation(nextLocation);
            return false;
        }
        return true;
    }, [confirmedNavigation]);
    
    const confirmNavigation = useCallback(() => {
        setShowPrompt(false);
        setConfirmedNavigation(true);
    }, []);
    useEffect(() => {
        if (confirmedNavigation && lastLocation) {
            navigate(lastLocation.location.pathname);
        }
    }, [confirmedNavigation, lastLocation]);
    useBlocker(handleBlockedNavigation, when);
    return [showPrompt, confirmNavigation, cancelNavigation];
}

export default useCallbackPrompt
Run Code Online (Sandbox Code Playgroud)

以上是我正在使用的两个文件。在 useBlocker.js 文件中,该特定行实际上导致了根本问题。请参考下图

在此输入图像描述

我正在使用"react-router-dom": "^6.3.0",这会引起任何问题吗?任何建议或修改都将受到高度赞赏。

Dre*_*ese 7

我无法使用 重现该问题react-router-dom@6.3.0,但当遇到react-router-dom@6.4.0. 我怀疑指定了依赖项,因为^6.3.0您实际上已经安装了更新的版本。如果您愿意,可以通过运行来检查已安装的版本npm list react-router-dom并自行验证。

v6.3.0 和 v6.4.0 之间的导航上下文似乎有轻微的破坏性变化。v6.3.0 版本是历史对象(source),而 v6.4.0 是新的导航上下文对象,其中navigator界面更简单(source)。

解决方案 1 - 恢复到以前的版本

6.3.0您可以通过运行npm i -s react-router-dom@6.3.0安装该确切版本来恢复到原来的状态。仔细检查您的 package.json 文件以确保条目为"react-router-dom": "6.3.0"

解决方案 2 - 使用“真实”history对象

如果您想继续使用较新的 RRD 版本,那么我建议的另一种选择是history@5 history直接使用该对象,而不是尝试使用react-router@6 navigator. 无论如何,RRDv6 只导出history方法的子集。

  1. 添加history@5为项目依赖项。

    重要提示:您需要检查react-router-dom正在使用的版本并匹配(如果可以的话)。

  2. 创建并导出自定义历史对象。createBrowserHistory对于 BrowserRouter、createHashHistory对于 HashRouter 等。

    import { createBrowserHistory } from 'history';
    
    const history = createBrowserHistory();
    
    export default history;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从 RRD 导入您的自定义历史对象和历史路由器。

    import { unstable_HistoryRouter as Router } from "react-router-dom";
    import history from './history';
    
    ...
    
    <Router history={history}>
      <App />
    </Router>
    
    Run Code Online (Sandbox Code Playgroud)
  4. 导入您的自定义历史对象以在自定义挂钩中使用。

    import { useCallback, useEffect, useState } from "react";
    import { useNavigate, useLocation } from "react-router-dom";
    import history from "./history"; // <-- import
    
    const useBlocker = (blocker, when = true) => {
      useEffect(() => {
        if (!when) return;
        const unblock = history.block((tx) => { // <-- use history
          const autoUnblockingTx = {
            ...tx,
            retry() {
              unblock();
              tx.retry();
            }
          };
          blocker(autoUnblockingTx);
        });
        return unblock;
      }, [blocker, when]);
    };
    
    Run Code Online (Sandbox Code Playgroud)

    useCallbackPrompt未受影响。

    const useCallbackPrompt = (when) => {
      const navigate = useNavigate();
      const location = useLocation();
      const [showPrompt, setShowPrompt] = useState(false);
      const [lastLocation, setLastLocation] = useState(null);
      const [confirmedNavigation, setConfirmedNavigation] = useState(false);
      const cancelNavigation = useCallback(() => {
        setShowPrompt(false);
      }, []);
    
      const handleBlockedNavigation = useCallback(
        (nextLocation) => {
          if (
            !confirmedNavigation &&
            nextLocation.location.pathname !== location.pathname
          ) {
            setShowPrompt(true);
            setLastLocation(nextLocation);
            return false;
          }
          return true;
        },
        [confirmedNavigation]
      );
    
      const confirmNavigation = useCallback(() => {
        setShowPrompt(false);
        setConfirmedNavigation(true);
      }, []);
      useEffect(() => {
        if (confirmedNavigation && lastLocation) {
          navigate(lastLocation.location.pathname);
        }
      }, [confirmedNavigation, lastLocation]);
      useBlocker(handleBlockedNavigation, when);
      return [showPrompt, confirmNavigation, cancelNavigation];
    };
    
    Run Code Online (Sandbox Code Playgroud)

演示

编辑在导航到其他页面时获取导航器块不是功能


Neo*_*eon 2

从 v6.4.0 开始,navigator.block 被删除。您可以在这里找到解决方法:https://gist.github.com/MarksCode/64e438c82b0b2a1161e01c88ca0d0355

另外,相关讨论也正在进行中。https://github.com/remix-run/react-router/issues/8139#issuecomment-1262630360

  • 哦,哇……那条线变成了真正的垃圾箱火灾。 (2认同)