我们有时候会遇到这种业务场景: 进入某个页面时,我们需要验证用户是否已经登陆,是否拥有足够权限?我们可以通过监听路由的变化来实现。但是在react下,怎么实现呢?
下面讨论实现过程:
- 手动实现
- react-router-watcher实现, 详细请见示例代码
手动实现
使用Hook方式实现:
import { useEffect, useState, useRef, useCallback, useMemo } from 'react'
import { useLocation, useHistory, useRouteMatch } from 'react-router'
const useWatcher = () => {
const location = useLocation()
const history = useHistory()
const match = useRouteMatch()
const [pathname, setPathname] = useState('')
const listeners = useRef([])
const addListener = useCallback((listener) => {
listeners.current.push(listener)
}, [])
const removeListener = useCallback((listener) => {
const index = listeners.current.findIndex(item => item === listener)
if (index >= 0) {
listeners.current.splice(index, 1)
}
}, [])
const execListeners = useCallback(() => {
listeners.current.forEach(func => {
func({
history,
match,
location
})
})
}, [history, match, location])
useEffect(() => {
if (pathname !== location.pathname) {
execListeners()
setPathname(pathname)
}
}, [location, execListeners, pathname])
const watcher = useMemo(() => {
return {
removeListener, addListener
}
}, [removeListener, addListener])
return watcher
}
export default useWatcher
使用react类实现:
import React, { Component } from 'react'
import { withRouter } from 'react-router'
class ReactRouterWatcher extends Component{
constructor(){
super()
this.listeners = []
this.addRouteChangeListener = this.addRouteChangeListener.bind(this)
this.removeRouteChangeListener = this.removeRouteChangeListener.bind(this)
}
addRouteChangeListener(listener){
this.listeners.push(listener)
}
removeRouteChangeListener(listener) {
const index = this.listeners.findIndex(item => item === listener)
if (index >= 0) {
this.listeners.splice(index, 1)
}
}
componentDidUpdate(){
this.execListeners()
}
componentDidMount(){
this.execListeners()
}
execListeners(){
this.listeners.forEach(func => {
func({
history: this.props.history,
match: this.props.match,
location: this.props.location
})
})
}
shouldComponentUpdate(nextProps){
return nextProps.location.pathname !== this.props.location.pathname
}
render() {
return this.props.children({
addRouteChangeListener: this.addRouteChangeListener,
removeRouteChangeListener: this.removeRouteChangeListener
})
}
}
const Watcher = withRouter(ReactRouterWatcher)
export default Watcher
export const withWatcher = (TargetComponent) => {
return (props) => (
<Watcher>
{
({addRouteChangeListener, removeRouteChangeListener}) => {
return <TargetComponent
addRouteChangeListener={addRouteChangeListener}
removeRouteChangeListener={removeRouteChangeListener}
{...props}
/>
}
}
</Watcher>
)
}
注意使用细节
无论使用以下哪种方式,都必须放在Router组件的孩子节点中。
使用之前,若不熟悉react-router,请最好先了解下react-router
API
ReactRouterWatcher
ReactRouterWatcher是一个headless风格的组件,children必须是一个函数, 该函数的形式是:
({
addRouteChangeListener, // 作用是添加监听函数,接受一个监听函数作为参数
removeRouteChangeListener // 作用是移除监听函数,接受一个监听函数作为参数
}) => {
// 必须返回一个react组件
return SomeReactComponent
}
使用方式:
<Router>
<ReactRouterWatcher>
{
({
addRouteChangeListener, //
removeRouteChangeListener
}) => {
return <Main addRouteChangeListener={addRouteChangeListener} removeRouteChangeListener={removeRouteChangeListener}/>
}
}
</ReactRouterWatcher>
<Router>
监听函数
监听函数形式如下:
/**
* history, match, location // 这三个参数与调用react-router的withRouter注入的参数一致
* @param history
* @param match
* @param location
**/
function({history, match, location}){
}
withWatcher
withWatcher是一个高阶函数,接受一个React组件作为参数,返回一个新的react组件,这个新的组件将被自动注入addRouteChangeListener, removeRouteChangeListener两个属性
使用方式:
const Content = withWatcher(Main)
<Router>
<Content />
</Router>
hook
const Content = () => {
const watcher = useRouteWatcher()
return <Main addRouteChangeListener={watcher.addListener} removeRouteChangeListener={watcher.removeListener}/>
}
<Router>
<Content />
</Router>
准备
假设存在一个Main.js组件:
import {
Switch,
Route,
useHistory
} from "react-router-dom";
import {useCallback, useEffect, useState} from 'react'
import routes from './router/router'
const Main = ({addRouteChangeListener, removeRouteChangeListener}) => {
const history = useHistory()
const [active, setActive] = useState('foo')
const setBar = useCallback(() => {
if (active === 'bar') return
setActive('bar')
history.push('/bar')
}, [active, history])
const setFoo = useCallback(() => {
if (active === 'foo') return
setActive('foo')
history.push('/foo')
}, [active, history])
const onRouteChange = useCallback((context) => {
console.log('onRouteChange----c--context----s--', context)
}, [])
useEffect(() => {
addRouteChangeListener(onRouteChange)
return () => removeRouteChangeListener && removeRouteChangeListener(onRouteChange)
// return () => {
// removeRouteChangeListener(onRouteChange)
// }
}, [addRouteChangeListener, onRouteChange, removeRouteChangeListener])
return (
<>
<div style={{height: 80, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<button style={active === 'foo' ? {color: 'blue', marginRight: 8} : {marginRight: 8}} onClick={setFoo}>Foo</button>
<button style={active === 'bar' ? {color: 'blue', marginRight: 8} : {marginRight: 8}} onClick={setBar}>Bar</button>
</div>
<Switch >
{
routes.map((item) => {
const Component = item.component
return (
<Route path={item.url} key={item.url} exact >
<Component />
</Route>
)
})
}
</Switch>
</>
)
}
export default Main
案例
使用ReactRouterWatcher组件
import {
HashRouter as Router
} from "react-router-dom";
import ReactRouterWatcher from 'react-router-watcher'
import Main from './Main'
import './App.css';
function App() {
return (
<Router>
<div className="App">
<ReactRouterWatcher>
{
({addRouteChangeListener, removeRouteChangeListener}) => {
return <Main addRouteChangeListener={addRouteChangeListener} removeRouteChangeListener={removeRouteChangeListener}/>
}
}
</ReactRouterWatcher>
</div>
</Router>
);
}
使用高阶函数withWatcher
import {
HashRouter as Router
} from "react-router-dom";
import { withWatcher } from 'react-router-watcher'
import Main from './Main'
import './App.css';
const Content = withWatcher(Main)
function App() {
return (
<Router>
<div className="App">
<Content />
</div>
</Router>
);
}
export default App;
使用hook
import React from 'react'
import {
HashRouter as Router
} from "react-router-dom";
import useRouteWatcher from 'react-router-watcher/build/hook'
import Main from './Main'
import './App.css';
const Content = () => {
const watcher = useRouteWatcher()
return <Main addRouteChangeListener={watcher.addListener} removeRouteChangeListener={watcher.removeListener}/>
}
function App() {
return (
<Router>
<div className="App">
<Content />
</div>
</Router>
);
}

本文介绍了在React应用中如何监听路由变化,以实现特定业务需求,如用户登录验证。通过手动实现、使用`react-router-watcher`库以及Hook方式,详细阐述了三种监听路由变化的方法,并提供了相关API和使用案例。
1981

被折叠的 条评论
为什么被折叠?



