如何监听 react router v4 中的路由变化?
How to listen to route changes in react router v4?
我有几个用作路线的按钮。每次更改路线时,我都想确保激活的按钮发生变化。
有没有办法在 React Router v4 中监听路由变化?
你应该使用 history v4 库。
示例来自 there
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
要扩展上述内容,您需要获取历史对象。如果您使用 BrowserRouter
,您可以导入 withRouter
并用 higher-order component (HoC) 包装您的组件,以便通过 props 访问历史对象的属性和函数。
import { withRouter } from 'react-router-dom';
const myComponent = ({ history }) => {
history.listen((location, action) => {
// location is an object like window.location
console.log(action, location.pathname, location.state)
});
return <div>...</div>;
};
export default withRouter(myComponent);
唯一需要注意的是 withRouter 和大多数其他访问 history
的方法似乎会污染道具,因为它们 de-structure 将对象放入其中。
正如其他人所说,这已被反应路由器暴露的钩子所取代,并且存在内存泄漏。如果您在功能组件中注册侦听器,您应该通过 useEffect 这样做,并在该功能的 return 中注销它们。
我使用 withRouter
来获取 location
道具。当组件因新路由而更新时,我检查值是否更改:
@withRouter
class App extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired
}
// ...
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
// ...
render(){
return <Switch>
<Route path="/" exact component={HomePage} />
<Route path="/checkout" component={CheckoutPage} />
<Route path="/success" component={SuccessPage} />
// ...
<Route component={NotFound} />
</Switch>
}
}
希望对您有所帮助
在某些情况下,您可能会使用 render
属性而不是 component
,这样:
class App extends React.Component {
constructor (props) {
super(props);
}
onRouteChange (pageId) {
console.log(pageId);
}
render () {
return <Switch>
<Route path="/" exact render={(props) => {
this.onRouteChange('home');
return <HomePage {...props} />;
}} />
<Route path="/checkout" exact render={(props) => {
this.onRouteChange('checkout');
return <CheckoutPage {...props} />;
}} />
</Switch>
}
}
请注意,如果您在 onRouteChange
方法中更改状态,这可能会导致 'Maximum update depth exceeded' 错误。
带挂钩:
import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'
const DebugHistory = ({ history }) => {
useEffect(() => {
console.log('> Router', history.action, history.location])
}, [history.location.key])
return null
}
DebugHistory.propTypes = { history: historyShape }
export default withRouter(DebugHistory)
导入并呈现为 <DebugHistory>
组件
withRouter
、history.listen
和 useEffect
(React Hooks)可以很好地协同工作:
import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
const Component = ({ history }) => {
useEffect(() => history.listen(() => {
// do something on route change
// for my example, close a drawer
}), [])
//...
}
export default withRouter(Component)
监听器回调将在路由更改时触发,history.listen
的 return 是一个关闭处理程序,可以很好地与 useEffect
配合使用。
import { useHistory } from 'react-router-dom';
const Scroll = () => {
const history = useHistory();
useEffect(() => {
window.scrollTo(0, 0);
}, [history.location.pathname]);
return null;
}
使用 useEffect
钩子可以在不添加侦听器的情况下检测路由更改。
import React, { useEffect } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import Main from './Main';
import Blog from './Blog';
const App = ({history}) => {
useEffect( () => {
// When route changes, history.location.pathname changes as well
// And the code will execute after this line
}, [history.location.pathname]);
return (<Switch>
<Route exact path = '/' component = {Main}/>
<Route exact path = '/blog' component = {Blog}/>
</Switch>);
}
export default withRouter(App);
v5.1 引入了有用的 hook useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselocation
import { Switch, useLocation } from 'react-router-dom'
function usePageViews() {
let location = useLocation()
useEffect(
() => {
ga.send(['pageview', location.pathname])
},
[location]
)
}
function App() {
usePageViews()
return <Switch>{/* your routes here */}</Switch>
}
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';
function MyApp() {
const location = useLocation();
useEffect(() => {
console.log('route has been changed');
...your code
},[location.pathname]);
}
带挂钩
使用 React Hooks,我正在使用 useEffect
import React from 'react'
const history = useHistory()
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
const [search, setSearch] = useState(parsed.search ? parsed.search : '')
useEffect(() => {
const parsedSearch = parsed.search ? parsed.search : ''
if (parsedSearch !== search) {
// do some action! The route Changed!
}
}, [location.search])
在这个例子中,我在路线改变时向上滚动:
import React from 'react'
import { useLocation } from 'react-router-dom'
const ScrollToTop = () => {
const location = useLocation()
React.useEffect(() => {
window.scrollTo(0, 0)
}, [location.key])
return null
}
export default ScrollToTop
我刚刚处理了这个问题,所以我将添加我的解决方案作为对给出的其他答案的补充。
这里的问题是 useEffect
并没有像您希望的那样真正工作,因为调用只在第一次渲染后被触发,所以会有不希望的延迟。
如果你使用一些像 redux 这样的状态管理器,你可能会因为商店中的挥之不去的状态而在屏幕上出现闪烁。
您真正想要的是使用 useLayoutEffect
,因为它会立即触发。
所以我写了一个小的实用函数,我把它放在和我的路由器相同的目录中:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
我是这样从组件 HOC 中调用的:
callApis(() => getTopicById({topicId}), path);
path
是在使用 withRouter
.
时在 match
对象中传递的道具
我不太赞成手动收听/取消收听历史记录。
那只是我。
对于功能组件,请尝试将 useEffect 与 props.location 结合使用。
import React, {useEffect} from 'react';
const SampleComponent = (props) => {
useEffect(() => {
console.log(props.location);
}, [props.location]);
}
export default SampleComponent;
我有几个用作路线的按钮。每次更改路线时,我都想确保激活的按钮发生变化。
有没有办法在 React Router v4 中监听路由变化?
你应该使用 history v4 库。
示例来自 there
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
要扩展上述内容,您需要获取历史对象。如果您使用 BrowserRouter
,您可以导入 withRouter
并用 higher-order component (HoC) 包装您的组件,以便通过 props 访问历史对象的属性和函数。
import { withRouter } from 'react-router-dom';
const myComponent = ({ history }) => {
history.listen((location, action) => {
// location is an object like window.location
console.log(action, location.pathname, location.state)
});
return <div>...</div>;
};
export default withRouter(myComponent);
唯一需要注意的是 withRouter 和大多数其他访问 history
的方法似乎会污染道具,因为它们 de-structure 将对象放入其中。
正如其他人所说,这已被反应路由器暴露的钩子所取代,并且存在内存泄漏。如果您在功能组件中注册侦听器,您应该通过 useEffect 这样做,并在该功能的 return 中注销它们。
我使用 withRouter
来获取 location
道具。当组件因新路由而更新时,我检查值是否更改:
@withRouter
class App extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired
}
// ...
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
// ...
render(){
return <Switch>
<Route path="/" exact component={HomePage} />
<Route path="/checkout" component={CheckoutPage} />
<Route path="/success" component={SuccessPage} />
// ...
<Route component={NotFound} />
</Switch>
}
}
希望对您有所帮助
在某些情况下,您可能会使用 render
属性而不是 component
,这样:
class App extends React.Component {
constructor (props) {
super(props);
}
onRouteChange (pageId) {
console.log(pageId);
}
render () {
return <Switch>
<Route path="/" exact render={(props) => {
this.onRouteChange('home');
return <HomePage {...props} />;
}} />
<Route path="/checkout" exact render={(props) => {
this.onRouteChange('checkout');
return <CheckoutPage {...props} />;
}} />
</Switch>
}
}
请注意,如果您在 onRouteChange
方法中更改状态,这可能会导致 'Maximum update depth exceeded' 错误。
带挂钩:
import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'
const DebugHistory = ({ history }) => {
useEffect(() => {
console.log('> Router', history.action, history.location])
}, [history.location.key])
return null
}
DebugHistory.propTypes = { history: historyShape }
export default withRouter(DebugHistory)
导入并呈现为 <DebugHistory>
组件
withRouter
、history.listen
和 useEffect
(React Hooks)可以很好地协同工作:
import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
const Component = ({ history }) => {
useEffect(() => history.listen(() => {
// do something on route change
// for my example, close a drawer
}), [])
//...
}
export default withRouter(Component)
监听器回调将在路由更改时触发,history.listen
的 return 是一个关闭处理程序,可以很好地与 useEffect
配合使用。
import { useHistory } from 'react-router-dom';
const Scroll = () => {
const history = useHistory();
useEffect(() => {
window.scrollTo(0, 0);
}, [history.location.pathname]);
return null;
}
使用 useEffect
钩子可以在不添加侦听器的情况下检测路由更改。
import React, { useEffect } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import Main from './Main';
import Blog from './Blog';
const App = ({history}) => {
useEffect( () => {
// When route changes, history.location.pathname changes as well
// And the code will execute after this line
}, [history.location.pathname]);
return (<Switch>
<Route exact path = '/' component = {Main}/>
<Route exact path = '/blog' component = {Blog}/>
</Switch>);
}
export default withRouter(App);
v5.1 引入了有用的 hook useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselocation
import { Switch, useLocation } from 'react-router-dom'
function usePageViews() {
let location = useLocation()
useEffect(
() => {
ga.send(['pageview', location.pathname])
},
[location]
)
}
function App() {
usePageViews()
return <Switch>{/* your routes here */}</Switch>
}
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';
function MyApp() {
const location = useLocation();
useEffect(() => {
console.log('route has been changed');
...your code
},[location.pathname]);
}
带挂钩
使用 React Hooks,我正在使用 useEffect
import React from 'react'
const history = useHistory()
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
const [search, setSearch] = useState(parsed.search ? parsed.search : '')
useEffect(() => {
const parsedSearch = parsed.search ? parsed.search : ''
if (parsedSearch !== search) {
// do some action! The route Changed!
}
}, [location.search])
在这个例子中,我在路线改变时向上滚动:
import React from 'react'
import { useLocation } from 'react-router-dom'
const ScrollToTop = () => {
const location = useLocation()
React.useEffect(() => {
window.scrollTo(0, 0)
}, [location.key])
return null
}
export default ScrollToTop
我刚刚处理了这个问题,所以我将添加我的解决方案作为对给出的其他答案的补充。
这里的问题是 useEffect
并没有像您希望的那样真正工作,因为调用只在第一次渲染后被触发,所以会有不希望的延迟。
如果你使用一些像 redux 这样的状态管理器,你可能会因为商店中的挥之不去的状态而在屏幕上出现闪烁。
您真正想要的是使用 useLayoutEffect
,因为它会立即触发。
所以我写了一个小的实用函数,我把它放在和我的路由器相同的目录中:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
我是这样从组件 HOC 中调用的:
callApis(() => getTopicById({topicId}), path);
path
是在使用 withRouter
.
match
对象中传递的道具
我不太赞成手动收听/取消收听历史记录。 那只是我。
对于功能组件,请尝试将 useEffect 与 props.location 结合使用。
import React, {useEffect} from 'react';
const SampleComponent = (props) => {
useEffect(() => {
console.log(props.location);
}, [props.location]);
}
export default SampleComponent;