React 监听路由变化

32,194 阅读2分钟

在项目中有一些关键信息,需要及时更新,比如商城类项目的顶部通栏中的个人账户信息,需要在任何页面下都呈现最新数据,而一般情况下顶部通栏都是公共组件,一次引入各个页面通用。那么怎么在每次路由切换的时候更新数据呢?

我最开始使用的是最暴力的方式,利用 Route 的 render 属性,在路由切换时先更新数据再渲染组件。

 <Route
    render={props => {
        getUserInfo(); // 更新数据的方法
        return <Header />
    }}
/>

这个方法看似解决了需求的问题,但是有个非常明显的弊端,就是路由每次变化都要重新渲染组件,其实有很多的的渲染是完全没必要的,但是这种情况下并没有办法进行拦截,而且即使是相同的路由,组件也会重新 render 导致进行了没必要的接口请求和组件渲染,一番折腾呵额尝试总结了以下几种更好的路由监听方案。

1. 路由下的内容组件中可以通过 history 对象来进行监听

class Header extends Component {
    componentDidMount() {
        // 监听路由的变化,如果路由发生变化则进行相应操作
        this.props.history.listen(location => {
            // 最新路由的 location 对象,可以通过比较 pathname 是否相同来判断路由的变化情况
            if (this.props.location.pathname !== location.pathname) {
               // 路由发生了变化
            }
        })
    }
}

2. 组件重新渲染前后

class Header extends Component {
    componentWillReceiveProps (nextProps, nextState) {
        if (this.props.location.pathname !== nextProps.location.pathname){
            // 路由发生了变化
        }
    }
}
    

同样 componentDidUpdatecomponentWillUpdate两个生命周期中也是能够去识别出路由变化的。

以上两种方式再结合 shouldComponentUpdate 可以减少组件不必要的渲染

class Header extends Component {
    // 当路由发生变化的时候再重新render
    shouldComponentUpdate(nextProps) {
        let prevRouteName = this.props.location.pathname;
        let currentRouteName = nextProps.location.pathname;
        return prevRouteName !== currentRouteName;
    }
}
    

3. hooks方式监听

import React, { useEffect } from 'React';
const Header = function (props) {
  useEffect(() => {
    console.log(props.location);
  }, [props.location])
}
export default Header;

其实监听路由最重要的就是监听 props 中 location 对象是否发生了变化,变化了则说明路由改变了,反正则不变。

监听的时机,一种是组件挂载后利用 this.props.history.listen ,另外一种是监听 props 的变化,这包括了 componentWillReceivePropscomponentDidUpdatecomponentWillUpdate,也就是组件接收新的 props,组件将要更新,组件更新完成这几个时间节点,hooks也是一样监听的是 props.location 的变化。

由于是旧项目没有使用 hooks,最终我使用了 this.props.history.listen + shouldComponentUpdate 的方式,在新项目中使用 hooks 将会变得更加的简单方便。