React 中 Link 和 NavLink 组件 activeClassName、activeStyle 属性不生效的问题

首先 导航链接应该使用  NavLink 而不再是  Link 

 NavLink 使用方法见 https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/NavLink.md

 

 NavLink 和  PureComponent 一起使用的时候,会出现 激活链接样式(当前页面对应链接样式,通过 activeClassName、activeStyle 设置) 不生效的情况。效果如下:

刷新页面后导航激活样式生效,点击导航链接的时候 url 跳转,但是导航激活样式不生效。

上图效果对应代码:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>NavLink And PureComponent</title>

  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  <script src="https://unpkg.com/react-router-dom/umd/react-router-dom.min.js"></script>

  <style>
    .menu-link {
      display: inline-block;
      width: 100px;
      text-decoration: none;
      text-align: center;
      background: #dedede;
    }
    .menu-link.active {
      background: tomato;
    }
  </style>
</head>
<body>
  <div id="app"></div>
  <script type="text/babel">
    // import ReactRouterDOM, { HashRouter, Route } from 'react-router-dom';
    // import React, { Component, PureComponent } from 'react';

    const { HashRouter, Route, NavLink } =  ReactRouterDOM;
    const { Component, PureComponent } =  React;

    class Menu extends PureComponent {
      render() {
        return (
          <div>
            <NavLink className="menu-link" activeClassName="active" activeStyle={{color: '#fff'}} to='/home'>首页</NavLink>
            <NavLink className="menu-link" activeClassName="active" activeStyle={{color: '#fff'}} to='/help'>帮助页</NavLink>
          </div>
        );
      }
    }

    class App extends PureComponent {
      render() {
        return (
          <HashRouter>
            <div>
            <Menu />
            <Route path='/home' component={ () => <div>首页内容</div> } />
            <Route path='/help' component={ () => <div>帮助页内容</div> } />
            </div>
          </HashRouter>
        );
      }
    }

    ReactDOM.render(
        <App />,
        document.getElementById('app')
    );
  </script>
</body>
</html>
复制代码

 

为什么不生效,我们在使用  PureComponent  之前应该知道 它相当于对组件的  props  和  state 进行浅比较,如果相等则不更新,以此来进行优化,防止多余更新。

而在上面的例子中 就相当于

复制代码
class Menu extends Component {
  shouldComponentUpdate(props, state) {
    console.log(props, this.props, props === this.props); // {} {} true
    console.log(state, this.state, state === this.state); // null null true
    // 由于 props 和 state 都不发生变化 下面的表达式会返回 false 组件不会发生更新 
    return !shallowEqual(props, this.props) || !shallowEqual(state, this.state);
  }

  render() {
    return (
      <div>
        <NavLink className="menu-link" activeClassName="active" activeStyle={{color: '#fff'}} to='/home'>首页</NavLink>
        <NavLink className="menu-link" activeClassName="active" activeStyle={{color: '#fff'}} to='/help'>帮助页</NavLink>
      </div>
    );
  }
}
复制代码

 

其中浅比较函数 shallowEqual 的实现(https://blog.csdn.net/juzipidemimi/article/details/80892440)

复制代码
function is(x, y) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    // Added the nonzero y check to make Flow happy, but it is redundant,排除 +0 === -0的情况
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN,x和y都不是NaN
    return x !== x && y !== y;
  }
}

function shallowEqual(objA, objB) {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return true;
}
View Code
复制代码

 

所以组件  Menu 不会发生更新,所以其子组件  NavLink 自然也就不会被更新。

那么该如果解决这个问题呢?

最简单的当然就是使用  Component  替换  PureComponent  

 

如果不想更改 PureComponent 的话,可以通过给组件传入当前  location 作为属性来解决。

 NavLink 是通过监听当前所在 location 来更新链接样式的,所以如果能在 location 改变的时候,更新组件就可以了,而做到这一点,只需要把  location 作为一个属性传入组件。

最简单的办法,把导航组件也作为一个 Route,并且能动态匹配所有路径,这样  location 会自动作为属性被注入到组件。

复制代码
class App extends PureComponent {
  render() {
    return (
      <HashRouter>
        <div>
        <Route path="/:place" component={Menu} />
        <Route path='/home' component={ () => <div>首页内容</div> } />
        <Route path='/help' component={ () => <div>帮助页内容</div> } />
        </div>
      </HashRouter>
    );
  }
}
复制代码

 

全文参考: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md

 

如果能帮助到你,点个赞呗 😂 

posted @   我不吃饼干呀  阅读(13733)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示