使用react-router检测路由更改

问题描述 投票:38回答:3

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

我想做的是这样的:

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

当URL更新时,有没有办法从react-router接收回调?

reactjs react-router react-router-v4 react-router-redux react-router-dom
3个回答
64
投票

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

history.listen()返回unlisten函数。你可以用它来听unregister

您可以配置您的路线

index.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

地点也可能具有以下属性:

location.state - 此位置的一些额外状态,不在URL中(在createBrowserHistorycreateMemoryHistory中受支持)

location.key - 表示此位置的唯一字符串(在createBrowserHistorycreateMemoryHistory中支持)

该操作是PUSH, REPLACE, or POP之一,具体取决于用户如何访问当前URL。

当您使用react-router v3时,您可以使用history.listen()包中的history,如上所述,或者您也可以使用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>
        )
    }
} 

9
投票

如果你想全局收听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';


0
投票

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

此解决方案不需要任何侦听器,它使用withRouter()componentDidUpdate()生命周期方法触发点击以在导航到新的URL路径时将ChromeVox集中在所需元素上。


履行

我创建了一个“屏幕”组件,它包含在react-router开关标签中,该标签包含所有应用程序屏幕。

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

Screen.tsx组件

注意:此组件使用React + TypeScript

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(),但是点击会导致ChromeVox停止阅读当前正在阅读的内容并重新开始我告诉它开始的地方。

高级注释:在此解决方案中,导航<nav>在屏幕组件内部并在<main>内容之后呈现,使用css main在视觉上定位在order: -1;上方。所以在伪代码中:

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

如果您对此解决方案有任何想法,意见或建议,请添加评论。

© www.soinside.com 2019 - 2024. All rights reserved.