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; }
所以组件 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
如果能帮助到你,点个赞呗 😂