Мне нужно реализовать некоторую бизнес-логику в зависимости от истории просмотров.
Я хочу сделать что-то вроде этого:
reactRouter.onUrlChange(url => {
this.history.push(url);
});
Есть ли способ получить обратный вызов от реактивного маршрутизатора при обновлении URL-адреса?
Ответы:
Вы можете использовать
history.listen()
функцию при попытке обнаружить изменение маршрута. Учитывая, что вы используетеreact-router v4
, оберните свой компонентwithRouter
HOC, чтобы получить доступ кhistory
опоре.history.listen()
возвращаетunlisten
функцию. Вы бы использовали это дляunregister
прослушивания.Вы можете настроить свои маршруты, например
index.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);
Из исторической документации :
Когда вы используете response-router v3, вы можете использовать
history.listen()
изhistory
пакета, как указано выше, или вы также можете использовать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> ) } }
источник
react-router v4
»withRouter
history.listen()
при использованииwithRouter
уже обновлений вашего компонента с новыми реквизитами каждый раз, когда происходит маршрутизация? Вы можете провести простое сравнениеnextProps.location.href === this.props.location.href
in,componentWillUpdate
чтобы выполнить все, что вам нужно, если оно изменилось.Обновление для React Router 5.1.
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> ); };
источник
Если вы хотите прослушать
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>
Еще лучше, если вы создадите объект истории как модуль, чтобы вы могли легко импортировать его в любое место, где он вам может понадобиться (например,
import history from './history';
источник
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
существует, прежде чем что-либо делать.источник
location
вот расположение браузера, так что оно одинаково для всех компонентов и всегда правильно в этом смысле. Если вы используете ловушку в разных компонентах, все они получат одинаковые значения при изменении местоположения. Я предполагаю, что то, что они делают с этой информацией, будет другим, но это всегда согласованно.Это старый вопрос, и я не совсем понимаю, зачем бизнесу нужно прислушиваться к изменениям маршрута, чтобы подтолкнуть его; кажется окольным.
НО, если вы оказались здесь, потому что все, что вам нужно, это обновить
'page_path'
изменение маршрута реагирующего маршрутизатора для аналитики Google / глобального тега сайта / чего-то подобного, вот крючок, который вы теперь можете использовать. Я написал это на основе принятого ответа: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> )
источник
Я столкнулся с этим вопросом, когда пытался сфокусировать программу чтения с экрана ChromeVox в верхней части «экрана» после перехода на новый экран в одностраничном приложении React. В основном это попытка имитировать то, что произошло бы, если бы эта страница была загружена путем перехода по ссылке на новую веб-страницу, отображаемую на сервере.
Это решение не требует никаких прослушивателей, оно использует
withRouter()
иcomponentDidUpdate()
метод жизненного цикла для запуска щелчка, чтобы сфокусировать ChromeVox на желаемом элементе при переходе к новому URL-адресу.Реализация
Я создал компонент «Экран», который обернут вокруг тега переключателя response-router, который содержит все экраны приложений.
<Screen> <Switch> ... add <Route> for each screen here... </Switch> </Screen>
Screen.tsx
Составная частьПримечание. Этот компонент использует React + TypeScript.
import React from 'react' import { RouteComponentProps, withRouter } from 'react-router' class Screen extends React.Component<RouteComponentProps> { public screen = React.createRef<HTMLDivElement>() public componentDidUpdate = (prevProps: RouteComponentProps) => { if (this.props.location.pathname !== prevProps.location.pathname) { // Hack: setTimeout delays click until end of current // event loop to ensure new screen has mounted. window.setTimeout(() => { this.screen.current!.click() }, 0) } } public render() { return <div ref={this.screen}>{this.props.children}</div> } } export default withRouter(Screen)
Я пробовал использовать
focus()
вместоclick()
, но щелчок заставляет ChromeVox перестать читать все, что он сейчас читает, и начать снова с того места, где я сказал ему начать.Дополнительное примечание: в этом решении навигация,
<nav>
которая находится внутри компонента Screen и отображается после<main>
содержимого, визуально располагается надmain
использованием cssorder: -1;
. Итак, в псевдокоде:<Screen style={{ display: 'flex' }}> <main> <nav style={{ order: -1 }}> <Screen>
Если у вас есть мысли, комментарии или советы по поводу этого решения, добавьте комментарий.
источник
React Router V5
Если вы хотите, чтобы имя пути было строкой ('/' или 'пользователи'), вы можете использовать следующее:
// React Hooks: React Router DOM let history = useHistory(); const location = useLocation(); const pathName = location.pathname;
источник
import React from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import Sidebar from './Sidebar'; import Chat from './Chat'; <Router> <Sidebar /> <Switch> <Route path="/rooms/:roomId" component={Chat}> </Route> </Switch> </Router>
import { useHistory } from 'react-router-dom'; function SidebarChat(props) { **const history = useHistory();** var openChat = function (id) { **//To navigate** history.push("/rooms/" + id); } }
**//To Detect the navigation change or param change** import { useParams } from 'react-router-dom'; function Chat(props) { var { roomId } = useParams(); var roomId = props.match.params.roomId; useEffect(() => { //Detect the paramter change }, [roomId]) useEffect(() => { //Detect the location/url change }, [location]) }
источник