如何在react路由器v4中侦听路由变化?

我有几个按钮作为路径。每次改变路线时,我都想确保激活的按钮发生了变化。

有没有办法在react路由器v4中监听路由变化?

245660 次浏览

你应该使用v4历史 lib。

在那里的例子

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

要在上面的基础上展开,您需要获取history对象。如果你正在使用BrowserRouter,你可以导入withRouter并用高阶分量包装你的组件,以便通过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的方法似乎会污染道具,因为它们将对象解构到其中。

正如其他人所说,这已经被react路由器暴露的钩子所取代,并且它有内存泄漏。如果你在一个函数组件中注册监听器,你应该通过useEffect来做,然后在函数的返回中取消注册。

我使用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方法中改变状态,这可能会导致“最大更新深度超出”错误。

用钩子:

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>组件

withRouterhistory.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的返回值是一个关闭处理程序,可以很好地与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引入了有用的钩子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对象的道具。

我真的不赞成手动收听/不收听历史。

对于功能组件,请尝试使用props.location中的useEffect。

import React, {useEffect} from 'react';


const SampleComponent = (props) => {


useEffect(() => {
console.log(props.location);
}, [props.location]);


}


export default SampleComponent;
< p > React路由器v6 &反应钩子, 你需要使用useLocation而不是useHistory,因为它已被弃用

import { useLocation } from 'react-router-dom'
import { useEffect } from 'react'


export default function Component() {


const history = useLocation();


useEffect(() => {
console.log('> Router', history.pathname)
}, [history.pathname]);
}