如何监听react-router-dom v6中的路由变化

Scr*_*pts 56 javascript reactjs react-router-dom

我正在尝试将旧的 React router dom 代码迁移到 v6,我想知道如何监听路由更改,我现在正在使用useHistory

const history = useHistory()
//then
history.listen(...)
Run Code Online (Sandbox Code Playgroud)

我确实阅读了新文档,并且发现它useHistory已更改为useNavigate

const navigate = useNavigate()
//then
navigate.listen(...) // listen is not a function
Run Code Online (Sandbox Code Playgroud)

你能帮我找到一种方法来监听 v6 中的路由变化吗

// This is a React Router v6 app
import { useNavigate } from "react-router-dom";

function App() {
  let navigate = useNavigate();
  function handleClick() {
    navigate("/home");
  }
  return (
    <div>
      <button onClick={handleClick}>go home</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*dez 38

从文档(https://reactrouter.com/en/main/hooks/use-location)中,使用此钩子

let location = useLocation();

React.useEffect(() => {
  ga('send', 'pageview');
}, [location]);
Run Code Online (Sandbox Code Playgroud)


Dre*_*ese 28

navigate函数是一个函数,而不是像旧react-router-dom版本 5 的history对象那样的对象。

您仍然可以创建自定义history对象,但需要创建自定义路由器才能使用它。这允许您导入对象 history并创建侦听器。

创建一个自定义路由器示例,使用更高级别的路由器之一作为示例,了解它们如何管理位置和状态,即BrowserRouter

const CustomRouter = ({ history, ...props }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      {...props}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};
Run Code Online (Sandbox Code Playgroud)

在您的代码中创建自定义history对象以供新的自定义路由器和其他组件使用。确保您已history@5安装为项目依赖项。这与 RRDv6 使用的版本相同。如果您需要安装它,请运行npm i history@5将其添加到项目的依赖项中。

const history = createBrowserHistory();
export default history;
Run Code Online (Sandbox Code Playgroud)

使用您的路由器并将您的历史对象传递给它。

import CustomRouter from '../CustomRouter';
import history from '../myHistory';

...

<CustomRouter history={history}>
  ....
</CustomRouter>
Run Code Online (Sandbox Code Playgroud)

在您想要侦听位置更改的组件中,导入您的历史对象并listen像之前一样调用回调。

import history from '../myHistory';

...

useEffect(() => {
  const unlisten = history.listen((location, action) => {
    // ... logic
  });

  return unlisten;
}, []);
Run Code Online (Sandbox Code Playgroud)

如果需要,您还可以创建自己的自定义useHistory挂钩,仅返回历史对象。

更新

react-router-dom已开始HistoryRouter为这样的用例导出 a 。您无需导入低级Router并实现内部逻辑,而是导入unstable_HistoryRouter as HistoryRouter并传递自定义历史对象(内存、哈希等)。

import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import history from "../myHistory";

...

<HistoryRouter history={history}>
  ....
</HistoryRouter>
Run Code Online (Sandbox Code Playgroud)

RRDv6.4+ 的注释

如果您使用 RRDv6.4+ 并且不使用数据路由器那么好消息是至少仍然通过 RRDv6.8.0unstable_HistoryRouter导出。您可以在此处跟踪存储库中提交的问题。

如果您使用数据路由器,那么新的“不稳定”方法是navigate直接使用路由器对象的附加函数。

例子:

const CustomRouter = ({ history, ...props }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      {...props}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};
Run Code Online (Sandbox Code Playgroud)

我在使用history.listen6.4 和 6.8 版本之间的解决方案时得到了好坏参半的结果,因此最好关注相关问题,以了解 RRD 维护者所说的当前访问“历史记录”的“不稳定”方法。

  • 在去依赖数组中将 `useEffect` 与 `location.pathname` 一起使用不是更容易吗?即 ` useEffect(() =&gt; { // ...code }, [location.pathname])` (4认同)

小智 12

要添加到接受的答案(无法评论,没有足够的代表点),如果导航卸载了您尝试调用的组件,则通过依赖项数组中带有 location.pathname 的 useEffect 订阅历史记录将不起作用使用效果来自。


ana*_*912 5

如果您需要对由于后退按钮而导致的路线更改做出反应:

react-router-dom v6.8.0甚至更早的版本中,尝试将侦听器附加到history,将引发错误:A history only accepts one active listener

我了解到这react-router-dom似乎也在次要版本之间引入了很多变化,所以你应该使用像不安全和不稳定这样的词,比如unstable_HistoryRouter特别严重。如果你不是很幸运的话,它们迟早会破裂。

就我而言,我必须升级才能获得重新引入的可选路由参数,而UNSAFE_NavigationContext我的前同事决定使用,但不再工作了。

因此,这是一种高级方法,它允许您侦听路由器历史堆栈上的操作而无需自己将另一个侦听器附加到路由器。这很好,因为默认情况下它已经有一个侦听器,并且它只是没有公开,但从它派生的操作是公开的,这就足够了。

在下面的示例中,我们对每个更改中的更改做出反应location,我们检查它是否是由于某个POP操作(例如,使用浏览器的后退按钮时触发)引起的,然后执行任何操作。

import { useEffect } from "react";
import {
  Location,
  NavigationType,
  useLocation,
  useNavigationType,
} from "react-router-dom";

export const useBackListener = (callback: () => void) => {
  const location: Location = useLocation();
  const navType: NavigationType = useNavigationType();

  useEffect(() => {
    if (navType === "POP" && location.key !== "default") {
      if (someCondition === true) callback();
      else {
        doSomethingElse();
      }
    }
  }, [location]);
};
Run Code Online (Sandbox Code Playgroud)