监听react-router路由的变化

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

我们有时候会遇到这种业务场景: 进入某个页面时,我们需要验证用户是否已经登陆,是否拥有足够权限?我们可以通过监听路由的变化来实现。但是在react下,怎么实现呢?

下面讨论实现过程:

  1. 手动实现
  2. 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>
  );
}

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值