代码之家  ›  专栏  ›  技术社区  ›  Aris

使用react路由器检测路由更改

  •  158
  • Aris  · 技术社区  · 7 年前

    我必须根据浏览历史实现一些业务逻辑。

    我想做的是这样:

    reactRouter.onUrlChange(url => {
       this.history.push(url);
    });
    

    当URL更新时,有没有办法接收来自react路由器的回调?

    8 回复  |  直到 7 年前
        1
  •  160
  •   Jacob Brazeal    4 年前

    你可以利用 history.listen() 尝试检测路线变化时的功能。考虑到您正在使用 react-router v4 ,用 withRouter HOC访问 history

    返回一个 unlisten 作用你会用这个来 unregister 来自倾听。

    索引.js

    ReactDOM.render(
          <BrowserRouter>
                <AppContainer>
                       <Route exact path="/" Component={...} />
                       <Route exact path="/Home" Component={...} />
               </AppContainer>
            </BrowserRouter>,
      document.getElementById('root')
    );
    

    AppContainer.js

    class App extends Component {
      
      componentWillMount() {
        this.unlisten = this.props.history.listen((location, action) => {
          console.log("on route change");
        });
      }
      componentWillUnmount() {
          this.unlisten();
      }
      render() {
         return (
             <div>{this.props.children}</div>
          );
      }
    }
    export default withRouter(App);
    

    来自历史 docs :

    history.listen

    history.listen((location, action) => {
          console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
      console.log(`The last navigation action was ${action}`)
    })
    

    location对象实现了window.location的子集

    **location.pathname** - The path of the URL
    **location.search** - The URL query string
    **location.hash** - The URL hash fragment
    

    位置还可能具有以下属性:

    位置.状态 -此位置的一些额外状态不在URL中(在 createBrowserHistory createMemoryHistory )

    location.key -表示此位置的唯一字符串(支持 在里面 创建浏览器历史记录 创建内存历史 )

    PUSH, REPLACE, or POP 取决于用户如何 已到达当前URL。

    当您使用react router v3时,您可以利用 历史。listen() 历史 包装如上文所述,或者您也可以使用 browserHistory.listen()

    您可以像这样配置和使用路由

    import {browserHistory} from 'react-router';
    
    class App extends React.Component {
    
        componentDidMount() {
              this.unlisten = browserHistory.listen( location =>  {
                    console.log('route changes');
                    
               });
          
        }
        componentWillUnmount() {
            this.unlisten();
         
        }
        render() {
            return (
                   <Route path="/" onChange={yourHandler} component={AppContainer}>
                       <IndexRoute component={StaticContainer}  />
                       <Route path="/a" component={ContainerA}  />
                       <Route path="/b" component={ContainerB}  />
                </Route>
            )
        }
    } 
    
        2
  •  116
  •   onosendi    2 年前

    import { useEffect } from 'react';
    import { useLocation } from 'react-router-dom';
    
    function SomeComponent() {
      const location = useLocation();
    
      useEffect(() => {
        console.log('Location changed');
      }, [location]);
    
      ...
    }
    
        3
  •  83
  •   davnicwil    3 年前

    react-router v6

    在react路由器v6中,这可以通过组合 useLocation useEffect

    import { useLocation } from 'react-router-dom';
    
    const MyComponent = () => {
      const location = useLocation()
    
      React.useEffect(() => {
        // runs on location, i.e. route, change
        console.log('handle route change here', location)
      }, [location])
      ...
    }
    

    为了方便重用,您可以在自定义 useLocationChange

    // runs action(location) on location, i.e. route, change
    const useLocationChange = (action) => {
      const location = useLocation()
      React.useEffect(() => { action(location) }, [location])
    }
    
    const MyComponent1 = () => {
      useLocationChange((location) => { 
        console.log('handle route change here', location) 
      })
      ...
    }
    
    const MyComponent2 = () => {
      useLocationChange((location) => { 
        console.log('and also here', location) 
      })
      ...
    }
    

    如果您还需要查看更改时的上一条路线,可以与 usePrevious

    const usePrevious = (value) => {
      const ref = React.useRef()
      React.useEffect(() => { ref.current = value })
    
      return ref.current
    }
    
    const useLocationChange = (action) => {
      const location = useLocation()
      const prevLocation = usePrevious(location)
      React.useEffect(() => { 
        action(location, prevLocation) 
      }, [location])
    }
    
    const MyComponent1 = () => {
      useLocationChange((location, prevLocation) => { 
        console.log('changed from', prevLocation, 'to', location) 
      })
      ...
    }
    

    需要注意的是,上述所有火灾都发生在 正在装载的客户端路由以及后续更改。如果这是一个问题,请使用后一个示例并检查 prevLocation

        4
  •  18
  •   Fabian Schultz    7 年前

    如果你想听听 history 对象,您必须自己创建它并将其传递给 Router listen() 方法:

    // Use Router from react-router, not BrowserRouter.
    import { Router } from 'react-router';
    
    // Create history object.
    import createHistory from 'history/createBrowserHistory';
    const history = createHistory();
    
    // Listen to history changes.
    // You can unlisten by calling the constant (`unlisten()`).
    const unlisten = history.listen((location, action) => {
      console.log(action, location.pathname, location.state);
    });
    
    // Pass history to Router.
    <Router history={history}>
       ...
    </Router>
    

    如果您将历史对象创建为一个模块,那么就更好了,这样您就可以轻松地将其导入任何您可能需要的地方(例如。 import history from './history';

        5
  •  12
  •   Johnny Magrippis    4 年前

    'page_path' 您现在可以使用。我是根据公认的答案写的:

    useTracking.js

    import { useEffect } from 'react'
    import { useHistory } from 'react-router-dom'
    
    export const useTracking = (trackingId) => {
      const { listen } = useHistory()
    
      useEffect(() => {
        const unlisten = listen((location) => {
          // if you pasted the google snippet on your index.html
          // you've declared this function in the global
          if (!window.gtag) return
    
          window.gtag('config', trackingId, { page_path: location.pathname })
        })
    
        // remember, hooks that add listeners
        // should have cleanup to remove them
        return unlisten
      }, [trackingId, listen])
    }
    

    你应该用这个钩子 在你的应用程序中,靠近顶部但仍在路由器内的某个地方。我有一个 App.js 看起来是这样的:

    import * as React from 'react'
    import { BrowserRouter, Route, Switch } from 'react-router-dom'
    
    import Home from './Home/Home'
    import About from './About/About'
    // this is the file above
    import { useTracking } from './useTracking'
    
    export const App = () => {
      useTracking('UA-USE-YOURS-HERE')
    
      return (
        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      )
    }
    
    // I find it handy to have a named export of the App
    // and then the default export which wraps it with
    // all the providers I need.
    // Mostly for testing purposes, but in this case,
    // it allows us to use the hook above,
    // since you may only use it when inside a Router
    export default () => (
      <BrowserRouter>
        <App />
      </BrowserRouter>
    )
    
        6
  •  1
  •   Beau Smith    5 年前

    当我在React单页应用程序中导航到新屏幕后,试图将ChromeVox屏幕阅读器聚焦到“屏幕”顶部时,我遇到了这个问题。基本上是试图模拟如果通过链接到一个新的服务器呈现的网页来加载这个页面会发生什么。

    此解决方案不需要任何侦听器,它使用 withRouter() componentDidUpdate()


    我创建了一个“屏幕”组件,它被包裹在react路由器开关标签周围,该标签包含所有应用程序屏幕。

    <Screen>
      <Switch>
        ... add <Route> for each screen here...
      </Switch>
    </Screen>
    

    Screen.tsx 组成部分

    注:

    import React from 'react'
    import { RouteComponentProps, withRouter } from 'react-router'
    
    class Screen extends React.Component<RouteComponentProps> {
      public screen = React.createRef<HTMLDivElement>()
      public componentDidUpdate = (prevProps: RouteComponentProps) => {
        if (this.props.location.pathname !== prevProps.location.pathname) {
          // Hack: setTimeout delays click until end of current
          // event loop to ensure new screen has mounted.
          window.setTimeout(() => {
            this.screen.current!.click()
          }, 0)
        }
      }
      public render() {
        return <div ref={this.screen}>{this.props.children}</div>
      }
    }
    
    export default withRouter(Screen)
    
    

    我试过使用 focus() click()

    高级注释: 在此解决方案中,导航 <nav> 在屏幕组件内,并在 <main> 内容视觉定位在 main order: -1; 因此,在伪代码中:

    <Screen style={{ display: 'flex' }}>
      <main>
      <nav style={{ order: -1 }}>
    <Screen>
    

    如果您对此解决方案有任何想法、评论或提示,请添加评论。

        7
  •  1
  •   yogesh panchal    4 年前
    import React from 'react';
    import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
    import Sidebar from './Sidebar';
    import Chat from './Chat';
    
    <Router>
        <Sidebar />
            <Switch>
                <Route path="/rooms/:roomId" component={Chat}>
                </Route>
            </Switch>
    </Router>
    

    import { useHistory } from 'react-router-dom';
    function SidebarChat(props) {
        **const history = useHistory();**
        var openChat = function (id) {
            **//To navigate**
            history.push("/rooms/" + id);
        }
    }
    

    **//To Detect the navigation change or param change**
    import { useParams } from 'react-router-dom';
    function Chat(props) {
        var { roomId } = useParams();
        var roomId = props.match.params.roomId;
    
        useEffect(() => {
           //Detect the paramter change
        }, [roomId])
    
        useEffect(() => {
           //Detect the location/url change
        }, [location])
    }
    
        8
  •  0
  •   jefelewis    4 年前

      // React Hooks: React Router DOM
      let history = useHistory();
      const location = useLocation();
      const pathName = location.pathname;