我必须根据浏览历史实现一些业务逻辑。
我想做的是这样的:
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
,请使用withRouter
HOC包装您的组件以访问history
props。
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.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
看起来像这样的:
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>
)