我必须根据浏览历史实现一些业务逻辑。
我想做的是这样的:
reactRouter.onUrlChange(url => {
this.history.push(url);
});
URL更新后,有什么方法可以接收来自react-router的回调吗?
我必须根据浏览历史实现一些业务逻辑。
我想做的是这样的:
reactRouter.onUrlChange(url => {
this.history.push(url);
});
URL更新后,有什么方法可以接收来自react-router的回调吗?
Answers:
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);
从历史文档:
您可以使用收听当前位置的更改
history.listen
:
history.listen((location, action) => { console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`) console.log(`The last navigation action was ${action}`) })
location对象实现window.location接口的子集,包括:
**location.pathname** - The path of the URL **location.search** - The URL query string **location.hash** - The URL hash fragment
位置也可能具有以下属性:
location.state-此位置的某些其他状态,不在URL中(在
createBrowserHistory
和中 受支持createMemoryHistory
)
location.key
-代表此位置的唯一字符串(createBrowserHistory
和中支持createMemoryHistory
)该操作是
PUSH, REPLACE, or POP
取决于用户如何访问当前URL的操作之一。
当您使用react-router v3时,您可以使用如上所述的history.listen()
fromhistory
包,也可以使用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
incomponentWillUpdate
进行简单的比较,以执行更改后需要执行的所有操作。
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
在执行任何操作之前检查a是否存在。
location
这里澄清一下浏览器的位置,因此每个组件中的位置都相同,并且在这种意义上始终正确。如果在不同的组件中使用该挂钩,则它们在位置更改时都会收到相同的值。我想他们对该信息的处理方式会有所不同,但始终是一致的。
这是一个古老的问题,我不太了解聆听路线变更以推动路线变更的业务需求。好像是回旋处。
但是,如果您由于想要更新'page_path'
Google Google Analytics(分析)/全局站点标签/类似内容而在React-Router路由上进行更改而在这里结束,则现在可以使用此钩子。我根据公认的答案写了它:
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>
)
我在React单页应用程序中导航到新屏幕后试图将ChromeVox屏幕阅读器聚焦到“屏幕”顶部时遇到了这个问题。基本上,尝试通过链接到新的服务器呈现的网页的链接来模拟如果加载此页面时将发生的情况。
该解决方案不需要任何监听器,它使用withRouter()
和componentDidUpdate()
生命周期的方法来触发点击导航到一个新的URL路径时关注的ChromeVox所需的元素。
我创建了一个“屏幕”组件,该组件包裹在包含所有应用程序屏幕的react-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
使用CSS上方order: -1;
。因此在伪代码中:
<Screen style={{ display: 'flex' }}>
<main>
<nav style={{ order: -1 }}>
<Screen>
如果您对此解决方案有任何想法,意见或建议,请添加评论。
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])
}