如何在 react-router 中为 Link 或 IndexLink 的包装元素设置 activeClassName?

Posted

技术标签:

【中文标题】如何在 react-router 中为 Link 或 IndexLink 的包装元素设置 activeClassName?【英文标题】:How to set activeClassName for wrapper element of Link or IndexLink in react-router? 【发布时间】:2016-05-05 08:05:29 【问题描述】:

我是 ReactJS 世界的新手,想知道如何将活动类名传递给 <li> 元素而不是 <a>(Link) 元素。

现在我有这种代码。单击时锚类会发生变化。

<li><IndexLink to='/' activeclassName='active'>A</IndexLink></li>
<li><Link to='/b' activeclassName='active'>B</Link></li>
<li><Link to='/c' activeclassName='active'>C</Link></li>

但我想得到类似的东西:

<li activeclassName='active'><IndexLink to='/'>A</IndexLink></li>
<li activeclassName='active'><Link to='/b'>B</Link></li>
<li activeclassName='active'><Link to='/c'>C</Link></li>

提前致谢

【问题讨论】:

v4版本请查看此链接***.com/a/54573453/1074944 【参考方案1】:

我不知道原因,但对我来说 &lt;NavLink activeClassName="active" to="/events"&gt;&lt;li&gt;Events&lt;/li&gt;&lt;/NavLink&gt; 根本不起作用。

经过大量谷歌搜索,我在 `react-router-dom 提供的 useLocation() 属性的帮助下找到了解决方法。

import React from 'react';
import  useLocation  from 'react-router-dom';

// Active the Link on two URLs

export default () => 
    
    // extract pathname from location
    const  pathname  = useLocation();

    return (
        <li className='Navigation__list-item'> 
            <NavLink 
                to="/events" 
                className=pathname === "/events" ? "active" : "" >
                Events 
            </NavLink> 
        </li>
    );
;

【讨论】:

【参考方案2】:

我正在使用 React Router v4.2,我无法从包装组件中获取对路由器对象的引用,因为上下文不可用。

这不起作用:

const  router  = this.context

我喜欢@mpen 的answer,但我使用的是嵌套路由,我不想更改定义路由组件的文件。

我所做的是将 location.pathname 与:

const NavItem = withRouter(props => 
  const  to, children, location  = props;
  return (
    <li className=location.pathname == to ? "active" : null>
      <Link to=to>children</Link>
    </li>
  );
);

【讨论】:

【参考方案3】:

使用 React Router v4,我只是通过在 NavLink 组件中包含 &lt;li&gt; 标记来使其工作。使用 &lt;li&gt; 标签包裹链接的解决方案导致 HOME &lt;li&gt; 标签始终具有 active 类。

import React from 'react'
import  NavLink  from 'react-router-dom';

class Header extends React.Component 

  render() 
    return (
      <div>
        <header>

          <nav>
            <ul>
              <NavLink activeClassName="active" exact=true to="/"><li>Home</li></NavLink>
              <NavLink activeClassName="active" to="/about"><li>About</li></NavLink>
              <NavLink activeClassName="active" to="/courses"><li>Courses</li></NavLink>
            </ul>
          </nav>

        </header>
      </div>
    )
  


export default Header

我相应地调整了lia CSS 选择器。

【讨论】:

【参考方案4】:

我没有使用&lt;Link /&gt;,而是使用&lt;NavLink /&gt;,它也可以。

import React,  Component  from 'react';
import  NavLink  from 'react-router-dom';

//.....

export default class AppNav extends Component 

    render ()
        return (
                <header>
                    <ul className="main-nav">
                        <li><NavLink activeClassName="active" exact=true to="/">Home</NavLink></li>
                        <li><NavLink activeClassName="active" to="/about">About</NavLink></li>
                        <li><NavLink activeClassName="active" to="/courses">Courses</NavLink></li>
                    </ul>
                </header>
        );
    

【讨论】:

这解决了我的问题。我想知道它们之间有什么区别使它起作用? 为我工作,不需要任何解决方法,应该将其标记为最佳答案。 是否设置了父&lt;li&gt;元素的类?对我来说,它只是设置从&lt;NavLink&gt; 创建的&lt;a&gt; 类。 这不是问题。他询问了父类。 OP 想要将类添加到 Link 的父元素,而不是 Link 本身。【参考方案5】:

其他答案似乎在 React Router v4 中不起作用。你可以这样做:

import React, PropTypes from 'react'
import Route, Link from 'react-router-dom'
import styles from './styles.less';

export default function NavItem(children, to, exact) 
    return (
        <Route path=to exact=exact children=(match) => (
            <li className=match ? styles.activeRoute : null>
                <Link to=to>children</Link>
            </li>
        )/>
    )


NavItem.propTypes = 
    to: PropTypes.string.isRequired,
    exact: PropTypes.bool,
    children: PropTypes.node.isRequired,
;

【讨论】:

谢谢你。我会将...props 添加到参数中,并在&lt;Link to=to ...props&gt;children&lt;/Link&gt; 中使用它以更灵活。 我不会使用三元运算符,而是使用 &lt;li className=match &amp;&amp; styles.activeRoute&gt; 更简洁。 @Sag1v 我不再喜欢使用那种语法了。当它评估为0 时,我得到了一点,我的 UI 中有一个杂散的 0。如果您不小心,可能会产生更严重的错误。 @mpen 我知道你的意思,虽然在这种情况下0 会产生相同的效果,不是吗? 更新了 jsfiddle 以获得@mpen 上面给出的答案:jsfiddle.net/69z2wepo/99537【参考方案6】:

使用 react 15.1.0、react-router 2.5.0 和 bootstrap 3.3(这不太重要),我开发了这个解决方案来激活链接:

npm install --save classnames

npm install --save lodash

组件:

import React from 'react';
import  Link, IndexLink  from 'react-router';
import _ from 'lodash';
import classnames from 'classnames';

class NavItem extends React.Component 
  constructor(props) 
    super(props);

    // The default state
    this.state = 
      isActive: false,
      unregisterRouteListener: false
    ;

    // Binding for functions
    this.locationHasChanged = this.locationHasChanged.bind(this);
  

  componentDidMount() 
    // Check if component is active on mount and add listener on route change
    this.setState(
      isActive: this.context.router.isActive(this.props.to, true),
      unregisterRouteListener: this.context.router.listen(this.locationHasChanged)
    );
  

  componentWillUnmount() 
    if (this.state.unregisterRouteListener) 
      // Remove the listener
      this.state.unregisterRouteListener();
    

    // Reset the state
    this.setState(
      isActive: false,
      unregisterRouteListener: false
    );
  

  // Update the state of the component, based on the router path
  locationHasChanged() 
    this.setState(
      isActive: this.context.router.isActive(this.props.to, true)
    );
  

  render () 
    let  index  = this.props;
    let LinkComponent = index ? Link : IndexLink;
    let newProps = _.omit(this.props, 'router');

    return (
      <li className=classnames('', this.state.isActive ? 'active' : '' )>
        <LinkComponent ...newProps>
          this.props.children
        </LinkComponent>
      </li>
    );
  


NavItem.contextTypes = 
  router: React.PropTypes.object
;

export default NavItem;

用法:

<NavItem to="/list">List</NavItem>

我是 React 的初学者,因此上述解决方案肯定需要改进,并且可能包含方法错误。但是,它也可能包含有用的信息或感兴趣的人的起点。

非常欢迎任何反馈或建议。谢谢! :)

【讨论】:

【参考方案7】:

试试react-router-active-component。

由于 react 或 typescript 版本之间的不兼容(这绝对不是一个成熟的生态系统),我无法获得任何以前的答案来轻松工作,但是这个组件成功了,并且可以应用于除 @ 之外的其他元素987654322@如果需要:

import activeComponent from 'react-router-active-component'
let NavItem = activeComponent('li');
...
<NavItem to='/' onlyActiveOnIndex>Home</NavItem>
<NavItem to='/generate-keywords'>Generate keywords</NavItem>

【讨论】:

【参考方案8】:

您需要将您的&lt;li&gt; 封装为路由器感知组件:

import  Link, IndexLink  from 'react-router'

class NavItem extends React.Component 
  render () 
    const  router  = this.context
    const  index, onlyActiveOnIndex, to, children, ...props  = this.props

    const isActive = router.isActive(to, onlyActiveOnIndex)
    const LinkComponent = index ? Link : IndexLink

    return (
      <li className=isActive ? 'active' : ''>
        <LinkComponent ...props>children</LinkComponent>
      </li>
    )
  

用法:

<ul>
  <NavItem to='/' index=true>Home</NavItem>
  <NavItem to='/a'>A</NavItem>
</ul>

我从 react-router-bootstrap 模块 https://github.com/react-bootstrap/react-router-bootstrap/blob/master/src/LinkContainer.js 中获得灵感。我没有测试它,所以让我知道它是怎么回事。

【讨论】:

根据您的设置,您可能需要NavLi.contextTypes = router: React.PropTypes.object ; export default NavLi; 您可能需要确保您的组件获取上下文,例如router: PropTypes.object.isRequired 不是通过上下文获取router,而是使用react-router包中的withRouter HOC通过props获取路由器。这也将在页面更改时更新链接的活动状态,否则 React 无法知道 isActive 是否已更改。 withRouter(...) 对我不起作用。 this.context.router 仍未定义 :(【参考方案9】:
/* Make sure that `location` is injected into this component */
<ul className="nav navbar-nav">
  <li className=location.pathname === '/' && 'active'>
    <Link to='/'>
      Home page
    </Link>
  </li>
  <li className=location.pathname.startsWith('/about') && 'active'>
    <Link to='/about'>
      About us
    </Link>
  </li>
</ul>

【讨论】:

最简单的答案。【参考方案10】:
/**
 * A navigation component
 */
import React,  Component  from 'react'
import  Link, IndexLink, withRouter  from 'react-router'

import styles from './styles.scss'

class NavItem extends Component 
  render () 
    const  router  = this.props
    const  index, to, children, ...props  = this.props

    let isActive
    if( router.isActive('/',true) && index ) isActive = true
    else  isActive = router.isActive(to)
    const LinkComponent = index ?  IndexLink : Link

    return (
      <li className=isActive ? 'active' : ''>
        <LinkComponent to=to ...props>children</LinkComponent>
      </li>
    )
  


NavItem = withRouter(NavItem)

export default NavItem

用法:

<ul className="nav nav-tabs"> 
  <NavItem to='/home' index=true >Home</NavItem>
  <NavItem to='/about'>About</NavItem>
</ul>

【讨论】:

虽然乍一看与 Marc 的答案相似,但这个答案对我来说效果更好。 withRouter 似乎是当前版本访问路由器的方式。 这对我有用,但因为我使用的是 IndexLink,所以我做了一个更改: else isActive = router.isActive(to, true) 。我必须将 true 添加到 IsActive 函数,否则 isActive 总是为 '/' 返回 true。 没用:router 对我来说是未定义的,在 this.props 中找不到 :-(【参考方案11】:

很好的答案。

只需更改以下内容即可使其正常工作...

LinkComponent = index ? IndexLink : Link //If its true you want an IndexLink 

//Also link needs to be...
<NavItem to="/" onlyActiveOnIndex index=true>Home</NavItem>

【讨论】:

以上是关于如何在 react-router 中为 Link 或 IndexLink 的包装元素设置 activeClassName?的主要内容,如果未能解决你的问题,请参考以下文章

react-router:如果 <Link> 处于活动状态,如何禁用它?

如何从文本呈现 react-router Link 组件?

ReactJS + React Router:如何从 React-Router 的 <Link> 禁用链接?

Antd Menu组件应该如何结合react-router Link组件?

通过 React-Router v4 <Link /> 传递值

如何使用 React-Router 的服务器端渲染中的 <Link> 标签更改 StaticRouter 的位置道具