我必须根据浏览历史实现一些业务逻辑。
我想做的是这样的:
reactRouter.onUrlChange(url => {
   this.history.push(url);
});
当 URL 更新时,有没有办法从 react-router 接收回调?
我必须根据浏览历史实现一些业务逻辑。
我想做的是这样的:
reactRouter.onUrlChange(url => {
   this.history.push(url);
});
当 URL 更新时,有没有办法从 react-router 接收回调?
您可以history.listen()在尝试检测路线变化时使用功能。考虑到您正在使用react-router v4,请使用withRouterHOC包装您的组件以访问historyprops。
history.listen()返回一个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);
从历史文档:
您可以使用以下命令侦听当前位置的更改
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 中(在
createBrowserHistory和 中 支持createMemoryHistory)
location.key- 表示此位置的唯一字符串(在createBrowserHistory和 中支持createMemoryHistory)该操作是一种
PUSH, REPLACE, or POP取决于用户如何到达当前 URL 的操作。
当您使用 react-router v3 时,您可以使用上面提到的history.listen()fromhistory包,也可以使用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>
        )
    }
} 
import React from 'react';
import { useLocation, Switch } from 'react-router-dom'; 
const App = () => {
  const location = useLocation();
  React.useEffect(() => {
    console.log('Location changed');
  }, [location]);
  return (
    <Switch>
      {/* Routes go here */}
    </Switch>
  );
};
react-router v6在即将发布的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在执行任何操作之前检查 a 是否存在。
如果要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>
如果您将历史对象创建为module,那就更好了,这样您就可以轻松地将它导入到您需要的任何地方(例如 import history from './history';
这是一个老问题,我不太了解监听路由更改以推动路由更改的业务需求;似乎迂回。
但是,如果您最终来到这里是因为您想要的只是更新'page_path'Google 分析/全局站点标签/类似内容的 react-router 路由更改,这里有一个您现在可以使用的钩子。我根据接受的答案写了它:
useTracking.jsimport { 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看起来像这样的:
App.jsimport * 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>
)