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

 

如果能幫助到你,點個贊唄 😂 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM