如何使用 react-router v4 检测路由变化?

Posted

技术标签:

【中文标题】如何使用 react-router v4 检测路由变化?【英文标题】:How to detect route changes with react-router v4? 【发布时间】:2018-12-29 18:59:55 【问题描述】:

我需要检测是否发生路由更改,以便将变量更改为 true。

我浏览了这些问题: 1.https://github.com/ReactTraining/react-router/issues/3554 2.How to listen to route changes in react router v4? 3.Detect Route Change with react-router

没有一个对我有用。发生路由变化时是否有明确的方法来调用函数。

【问题讨论】:

您能否详细说明您的用例?也许有另一种解决方案,而不是监听路由变化? 我正在使用 socket.io,但是有多个页面。 socket.io 提供的断开连接功能无法区分路由更改和用户离开整个站点之间的区别。如果我可以判断没有路线更改,那么我知道用户在使用 window.onbeforeunload 时正在离开站点。 @sn42 【参考方案1】:

一种方法是使用withRouter 高阶组件。

Live demo(点击超链接更改路线并在显示的控制台中查看结果)

您可以通过 withRouter 高阶组件访问历史对象的属性和最接近的匹配项。 withRouter 将在渲染时将更新的匹配、位置和历史道具传递给包装的组件。

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md

import withRouter from 'react-router-dom';

class App extends Component 
    componentDidUpdate(prevProps) 
        if (this.props.location.pathname !== prevProps.location.pathname) 
            console.log('Route change!');
        
    
    render() 
        return (
            <div className="App">
                ...routes
            </div>
        );
    


export default withRouter(props => <App ...props/>);

另一个使用 url 参数的例子:

如果您将个人资料路由从 /profile/20 更改为 /profile/32

你的路线被定义为/profile/:userId

componentDidUpdate(prevProps) 
    if (this.props.match.params.userId !== prevProps.match.params.userId) 
        console.log('Route change!');
    

【讨论】:

我的路线改变了:从 /profile 到 /conversations 或 /tour 到 /matches。 @wdm @Hunter690 您是否需要具体知道您更改了哪条路线或只是该路线已更改? 只是我的路线改变了 @Hunter690 查看编辑我使用withRouter添加了另一个示例 我尝试使用 withRouter,但由于某种原因我无法让 componentDidUpdate 运行(我添加了一个 console.log,但在控制台上没有得到任何东西)【参考方案2】:

如何跟踪应用程序状态中历史对象的长度?每次遍历新路由时,react-router 提供的历史对象的长度都会增加。见下图。

ComponentDidMountComponentWillUnMount 检查:

【讨论】:

我试图这样做,但我必须知道它是否在下一页加载之前更改了路线,因为我正在使用 window.onbeforeunload。 您可以在componentWillUnmount中查看长度是否增加。您可以在 componentDidMount 中初始化长度并检查您是否要在 componentWillUnmount 中前往另一条路线,因为在安装新组件之前长度会增加……请参见第二个屏幕截图。希望它有效! 有趣的想法;我试图在componentDidMountcomponentWillUnmount 中打印出history,但控制台什么也没输出。我有另一个问题;在第二个屏幕截图中,有一个Navigated to ... 打印出来;你知道如何在代码中访问它吗? 我不确定您是否可以从代码中访问它。它是 Chrome 开发工具的一部分:***.com/questions/28349285/… 你的路由组件在 props 上有history 吗?使用 React 开发工具进行验证以查看 上的道具。如果历史不在 props 上,可以从 Route path='tour' component=Tour 切换到 Route path='tour' render=(props) =&gt; &lt;Tour ...props /&gt; 以确保历史对象在您的路由组件上可用。【参考方案3】:

React 使用基于组件的架构。那么,我们为什么不遵守这条规则呢?

你可以看到DEMO。

每个页面都必须由 HOC 包装,这将自动检测页面的变化。

首页

import React from "react";
import  NavLink  from "react-router-dom";
import withBase from "./withBase";

const Home = () => (
  <div>
    <p>Welcome Home!!!</p>
    <NavLink to="/login">Go to login page</NavLink>
  </div>
);

export default withBase(Home);

withBase HOC

import React from "react";

export default WrappedComponent =>
  class extends React.Component 
    componentDidMount() 
      this.props.handleChangePage();
    

    render() 
      return <WrappedComponent />;
    
  ;

【讨论】:

【参考方案4】:

当组件被指定为&lt;Route&gt;component 属性时,React Router 4 (RR4) 会向它传递一些附加属性:matchlocationhistory

那么你应该使用componentDidUpdate生命周期方法来比较更新前后的location对象(记住ES对象比较规则)。由于位置对象是不可变的,它们永远不会匹配。即使你导航到同一个位置。

  componentDidUpdate(newProps) 
    if (this.props.location !== newProps.location) 
      this.handleNavigation();
    
  

withRouter 应该在您需要访问未指定为任何路由的component 属性的任意组件中的这些属性时使用。确保将您的应用包装在 &lt;BrowserRouter&gt; 中,因为它提供了所有必要的 API,否则这些方法将仅适用于 &lt;BrowserRouter&gt; 中包含的组件。

在某些情况下,用户决定通过导航按钮而不是浏览器中的专用界面重新加载页面。但是这样的比较:

this.props.location.pathname !== prevProps.location.pathname

会让它变得不可能。

【讨论】:

【参考方案5】:

使用 React Hooks,它应该很简单:

useEffect(() => 
    const  pathname  = location;
    console.log('New path:', pathname);
, [location.pathname]);

通过在第二个数组参数中传递location.pathname,意味着您要使用useEffect 仅在location.pathname 更改时重新运行。

带有代码源的实时示例:https://codesandbox.io/s/detect-route-path-changes-with-react-hooks-dt16i

【讨论】:

这对我不起作用。回调只被调用一次,以显示初始路径。但是再次更改路径名后,控制台中不会输出新路径。 我刚刚在 Codesandbox 上制作了一个简单的 React 应用程序,它的工作原理非常棒,请查看:codesandbox.io/s/… 我尝试了相同的代码,但它在我的应用程序中不起作用...我不知道...谢谢。我会投票的:) 谢谢,您可以与我分享您的代码,以便我提供帮助:) 如果hashsearch 发生变化怎么办?【参考方案6】:

React Router v5 现在通过钩子自动检测路由更改。后面是the example from the team:

import  Switch, useLocation  from 'react-router'

function usePageViews() 
  let location = useLocation()

  useEffect(
    () => 
      ga.send(['pageview', location.pathname])
    ,
    [location]
  )


function App() 
  usePageViews()
  return <Switch>/* your routes here */</Switch>

此示例会在每次 URL 更改时向 Google Analytics (ga) 发送“页面浏览量”。

【讨论】:

问题是它在同一条路线上更新,因为 location.key 每次都在变化。如果您需要在效果中监听 location.pathname 的任何内容

以上是关于如何使用 react-router v4 检测路由变化?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 react-router v4 的根路由上设置可选参数?

react-router v4 使用 history 控制路由跳转

在 react-router v4 中对不同的路由路径使用相同的组件

在 react-router v4 中嵌套路由

在 react-router v4 中将自定义道具传递给路由器组件

在 react-router v4 中将自定义道具传递给路由器组件