Ответы:
Я использую , 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
, вы можете импортировать withRouter
и обернуть свой компонент компонентом более высокого порядка (HoC) , чтобы иметь доступ через реквизиты к свойствам и функциям объекта истории.
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
к объекту, похоже, загрязняют свойства, поскольку они объект в нем.
withRoutes
в withRouter
.
Вам следует использовать history 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
. См. Пример использования базового URL-адреса в документации по истории v4 . Поскольку на history
самом деле это оболочка собственного history
объекта браузера, она не ведет себя точно так же, как собственный объект.
const unlisten = history.listen(myListener); unlisten();
withRouter
, history.listen
И useEffect
(React Ловушки) работает довольно хорошо вместе:
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]);
}
с крючками
С помощью react Hooks я использую 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
атрибут вместо 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
.
Я не совсем сторонник того, чтобы вручную слушать / отменять историю. Это просто имо.