Answers:
我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>
}
}
希望能帮助到你
withRouter
,但是出现错误You should not use <Route> or withRouter() outside a <Router>
。我没有<Router/>
在上面的代码中看到任何组件。那么它是如何工作的呢?
<Switch>
组件充当了事实上的路由器。只有第一个<Route>
具有匹配路径的条目才会被渲染。<Router/>
在这种情况下,不需要任何组件
为了扩展上述内容,您将需要获取历史对象。如果使用BrowserRouter
,则可以使用高阶组件(HoC)导入withRouter
和包装组件,以便通过prop访问历史对象的属性和功能。
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);
唯一需要注意的是,使用Router和其他大多数访问方法history
似乎会在道具分解对象时污染道具。
withRoutes
到withRouter
。
您应该使用历史记录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.push
确实触发了history.listen
。请参阅历史记录v4文档中的“ 使用基本URL”示例。因为那实际上是浏览器本机对象的包装,所以它的行为与本机对象并不完全相同。history
history
const unlisten = history.listen(myListener); unlisten();
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
是与配合良好的关机处理程序useEffect
。
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 { 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>
组件
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]);
}
带钩
随着反应挂钩,我正在使用 useEffect
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])
在某些情况下,您可以通过以下方式使用render
attribute而不是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
方法中更改状态,则可能导致“超出最大更新深度”错误。
使用该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);
我只是处理了这个问题,所以我将在其他给出的答案的基础上增加解决方案。
这里的问题是,useEffect
它实际上并没有按您希望的那样工作,因为调用仅在第一次渲染后才触发,因此会出现不必要的延迟。
如果您使用诸如redux之类的状态管理器,由于商店中的状态持续存在,您很可能会在屏幕上闪烁。
您真正想要的是使用它,useLayoutEffect
因为它会立即触发。
因此,我编写了一个小的实用程序函数,并将其放在与路由器相同的目录中:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
我从组件HOC内这样调用:
callApis(() => getTopicById({topicId}), path);
path
是match
使用时会在对象中传递的道具withRouter
。
我不太赞成手动收听/取消收听历史记录。那只是imo。