我如何添加一个活跃的类从反应路由器的链接?

我使用 Link创建了一个引导样式的侧边栏:

<ul className="sidebar-menu">
<li className="header">MAIN NAVIGATION</li>
<li><Link to="dashboard"><i className="fa fa-dashboard"></i> <span>Dashboard</span></Link></li>
<li><Link to="email_lists"><i className="fa fa-envelope-o"></i> <span>Email Lists</span></Link></li>
<li><Link to="billing"><i className="fa fa-credit-card"></i> <span>Buy Verifications</span></Link></li>
</ul>

我想为包装元素 <li>上的 active活动路径设置类。我看到有其他的解决方案,显示了如何做到这一点,像 使用反应路由器当前路由在菜单上有条件地设置活动类,但我不认为这是最好的方式,设置一个活动类的包装到 Link

我也找到了 https://github.com/insin/react-router-active-component,但感觉它是不必要的。

在反应路由器,这是可能的,还是我需要使用一个外部解决方案?

199391 次浏览

On the Link component you can now add activeClassName or set activeStyle.

These allow you to easily add styles to the currently active link.


Previously, you could create a custom component that works like a wrapper to Link with the following logic.

In a file called nav_link.js

import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';


class NavLink extends React.Component {
render() {
var isActive = this.context.router.route.location.pathname === this.props.to;
var className = isActive ? 'active' : '';


return(
<Link className={className} {...this.props}>
{this.props.children}
</Link>
);
}
}


NavLink.contextTypes = {
router: PropTypes.object
};


export default NavLink;

And use it as given below in your component:

...
import NavLink from "./nav_link";
.....


<nav>
<ul className="nav nav-pills pull-right">
<NavLink to="/">
<i className="glyphicon glyphicon-home"></i> <span>Home</span>
</NavLink>
<NavLink to="about">
<i className="glyphicon glyphicon-camera"></i> <span>About</span>
</NavLink>
</ul>
</nav>

Answer updated with ES6:

import React, { Component } from 'react';
import { Link } from 'react-router'


class NavLink extends Component {
render() {
let isActive = this.context.router.isActive(this.props.to, true);
let className = isActive ? "active" : "";


return (
<li className={className}>
<Link {...this.props}/>
</li>
);
}
}


NavLink.contextTypes = {
router: React.PropTypes.object
};


export default NavLink;

Then use it as described above.

I didn't like the idea of creating a custom component, because if you have a different wrapping element you would have to create another custom component etc. Also, it is just overkill. So I just did it with css and activeClassName:

<li className="link-wrapper">  <!-- add a class to the wrapper -->
<Link to="something" activeClassName="active">Something</Link>
</li>

And then just add some css:

li.link-wrapper > a.active {
display: block;
width: 100%;
height:100%;
color: white;
background-color: blue;
}

Technically this doesn't style the li, but it makes the anchor fill the li and styles it.

This is my way, using location from props. I don't know but history.isActive got undefined for me

export default class Navbar extends React.Component {
render(){
const { location } = this.props;


const homeClass = location.pathname === "/" ? "active" : "";
const aboutClass = location.pathname.match(/^\/about/) ? "active" : "";
const contactClass = location.pathname.match(/^\/contact/) ? "active" : "";




return (
<div>
<ul className="nav navbar-nav navbar-right">
<li className={homeClass}><Link to="/">Home</Link></li>
<li className={aboutClass}><Link to="about" activeClassName="active">About</Link></li>
<li className={contactClass}><Link to="contact" activeClassName="active">Contact</Link></li>
</ul>


</div>
);}}

You can actually replicate what is inside NavLink something like this

const NavLink = ( {
to,
exact,
children
} ) => {


const navLink = ({match}) => {


return (
<li class=\{\{active: match}}>
<Link to={to}>
{children}
</Link>
</li>
)


}


return (
<Route
path={typeof to === 'object' ? to.pathname : to}
exact={exact}
strict={false}
children={navLink}
/>
)
}

just look into NavLink source code and remove parts you don't need ;)

React-Router V4 comes with a NavLink component out of the box

To use, simply set the activeClassName attribute to the class you have appropriately styled, or directly set activeStyle to the styles you want. See the docs for more details.

<NavLink
to="/hello"
activeClassName="active"
>Hello</NavLink>

The answer by Vijey has a bit of a problem when you're using react-redux for state management and some of the parent components are 'connected' to the redux store. The activeClassName is applied to Link only when the page is refreshed, and is not dynamically applied as the current route changes.

This is to do with the react-redux's connect function, as it suppresses context updates. To disable suppression of context updates, you can set pure: false when calling the connect() method like this:

//your component which has the custom NavLink as its child.
//(this component may be the any component from the list of
//parents and grandparents) eg., header


function mapStateToProps(state) {
return { someprops: state.someprops }
}


export default connect(mapStateToProps, null, null, {
pure: false
})(Header);

Check the issue here: reactjs#470

Check pure: false documentation here: docs

With react-router-dom@4.3.1 (though any 4.x.x should do I guess), we can use the withRouter HOC to accomplish this. For example, I want to implement the Bootstrap navbar and since it requires a class of active on <li class="nav-item"> and not on the anchor tag, I made a new component called NavItem to encapsulate a single li.nav-item. The implementation is as follows:

import React from "react";
import { Link, withRouter } from "react-router-dom";


const NavItem = ({ isActive, to, label }) => {
let classes = ["nav-item"];
if (isActive) classes.push("active");


return (
<li className={classes.join(" ")}>
<Link className="nav-link" to={to}>
{label}
</Link>
</li>
);
};


export default withRouter(({ location, ...props }) => {
const isActive = location.pathname === props.to;


console.log(location.pathname, props.to);


return <NavItem {...props} isActive={isActive} />;
});

As you can see, NavItem is just a stateless functional component which expects an isActive prop to determine whether active class should be added. Now, to update this prop as the location changes, we can make use of the withRouter HOC. You can pass any component to this function and it'll give it { match, location, history } objects in its props along with the ones you pass down. Here, I am creating a functional component inline which receives these objects and determines whether the current link is the active one using the location.pathname property. This'll give us a Boolean and we can return the NavItem along with isActive set to the value we computed using location.pathname.

A working example of this can be found here. Please let me know if there's an easier way to do this.

As of react-router-dom@4.3.1, we can just easily use the NavLink with activeClassName instead of Link. Example:

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


class NavBar extends Component {
render() {
return (
<div className="navbar">
<ul>
<li><NavLink to='/1' activeClassName="active">1</NavLink></li>
<li><NavLink to='/2' activeClassName="active">2</NavLink></li>
<li><NavLink to='/3' activeClassName="active">3</NavLink></li>
</ul>
</div>
);
}
}

Then in your CSS file:

.navbar li>.active {
font-weight: bold;
}

The NavLink will add your custom styling attributes to the rendered element based on the current URL.

Document is here

To set class on the active navigation element

import { NavLink } from 'react-router-dom';

&

<NavLink to="/Home" activeClassName="active">Home</NavLink>

Its very easy to do that, react-router-dom provides all.

import React from 'react';
import { matchPath, withRouter } from 'react-router';
    

class NavBar extends React.Component {
render(){
return(
<ul className="sidebar-menu">
<li className="header">MAIN NAVIGATION</li>
<li className={matchPath(this.props.location.pathname, { path: "/dashboard" }) ? 'active' : ''}><Link to="dashboard"><i className="fa fa-dashboard"></i>
<span>Dashboard</span></Link></li>
<li className={matchPath(this.props.location.pathname, { path: "/email_lists" }) ? 'active' : ''}><Link to="email_lists"><i className="fa fa-envelope-o"></i>
<span>Email Lists</span></Link></li>
<li className={matchPath(this.props.location.pathname, { path: "/billing" }) ? 'active' : ''}><Link to="billing"><i className="fa fa-credit-card"></i>
<span>Buy Verifications</span></Link></li>
</ul>
)
}
}
    

export default withRouter(NavBar);

Wrapping You Navigation Component with withRouter() HOC will provide few props to your component: 1. match 2. history 3. location

here i used matchPath() method from react-router to compare the paths and decide if the 'li' tag should get "active" class name or not. and Im accessing the location from this.props.location.pathname.

changing the path name in props will happen when our link is clicked, and location props will get updated NavBar also get re-rendered and active style will get applied

Since router v4 I am using 'refs' for setting the parent active class:

<ul>
<li>
<NavLink
innerRef={setParentAsActive}
activeClassName="is-active"
to={link}
>
{text}
</NavLink>
</ul>

NavLink's innerRef prop accepts callback function, which will receive DOM node as an argument. You can use then any DOM manipulation possible, in this case simply set parent element (<li>) to have the same class:

  const setParentAsActive = node => {
if (node) {
node.parentNode.className = node.className;
}
};

Drawbacks:

  • <a> will have unnecessary is-active class (as you only need it for <li>), or you can remove this class in the callback func.
  • if you change the element structure, f.e. wrap a tag inside a span, your callback will stop working, but it's possible to write more sofisticated DOM traverse function
  • you have to do some DOM manipulation

Just use NavLink rather than Link. It will add .active class automatically.

<Nav className="mr-auto">
<Nav.Link as={NavLink} to="/home">Home</Nav.Link>
<Nav.Link as={NavLink} to="/users">Users</Nav.Link>
</Nav>

Expanding on @BaiJiFeiLong's answer, add an active={} property to the link:

<Nav.Link as={Link} to="/user" active={pathname.startsWith('/user')}>User</Nav.Link>

This will show the User link as active when any path starts with '/user'. For this to update with each path change, include withRouter() on the component:

import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import Navbar from 'react-bootstrap/Navbar'
import Nav from 'react-bootstrap/Nav'


function Header(props) {
const pathname = props.location.pathname


return (
<Navbar variant="dark" expand="sm" bg="black">
<Navbar.Brand as={Link} to="/">
Brand name
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link as={Link} to="/user" active={pathname.startsWith('/user')}>User</Nav.Link>
<Nav.Link as={Link} to="/about" active={pathname.startsWith('/about')}>About</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}


export default withRouter(Header)   // updates on every new page

Using Jquery for active link:

$(function(){
$('#nav a').filter(function() {
return this.href==location.href
})
.parent().addClass('active').siblings().removeClass('active')


$('#nav a').click(function(){
$(this).parent().addClass('active').siblings().removeClass('active')
})
});

Use Component life cycle method or document ready function as specified in Jquery.

import React from 'react';
import {withRouter, Link} from "react-router-dom";


const SidenavItems = (props) => {
// create simple list of links
const items = [
{
type: "navItem",
icon: "home",
text: "Home",
link: "/",
restricted: false
},
{
type: "navItem",
icon: "user-circle",
text: "My Profile",
link: "/user",
restricted: false
},
{
type: "navItem",
icon: "sign-in",
text: "Login",
link: "/login",
restricted: false
},
];


const element = (item, i) => {  // create elements (Links)
// check if this is a current link on browser
let active = "";


if (props.location.pathname === item.link) {
active = "active";
}


return (
<div key={i} className={item.type}>
<Link
to={item.link}
className={active} // className will be set to "active"
>                      // or ""
{item.text}
</Link>
</div>
)
};


const showItems = () => {     // print elements
return items.map((item, i) => {
return element(item, i)
})
};


return (
<div>
{showItems()}  // print all the links we created in list
</div>
)
};
export default withRouter(SidenavItems);

One of the way you can use it

When you are using the Functional component then follow the instruction here.

  • add a variable in your component
  • create an event change browser URL change/or other change
  • re-assign the current path (URL)
  • use javascript match function and set active or others

Use the above code here.

import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';


const NavItems = () => {
let pathname = window.location.pathname;
useEffect(() => {
pathname = window.location.pathname;
}, [window.location.pathname]);


return (
<>
<li className="px-4">
<Link to="/home" className={`${pathname.match('/home') ? 'link-active' : ''}`}>Home</Link>
</li>
<li className="px-4">
<Link to="/about-me" className={`${pathname.match('/about-me') ? 'link-active' : ''}`}>About-me</Link>
</li>
<li className="px-4">
<Link to="/skill" className={`${pathname.match('/skill') ? 'link-active' : ''}`}>Skill</Link>
</li>
<li className="px-4">
<Link to="/protfolio" className={`${pathname.match('/protfolio') ? 'link-active' : ''}`}>Protfolio</Link>
</li>
<li className="pl-4">
<Link to="/contact" className={`${pathname.match('/contact') ? 'link-active' : ''}`}>Contact</Link>
</li>
</>
);
}


export default NavItems;

--- Thanks ---

Current React Router Version 5.2.0

activeStyle is a default css property of NavLink component which is imported from react-router-dom

So that we can write our own custom css to make it active.In this example i made background transparent and text to be bold.And I store it on a constant named isActive

    import React from "react";
import { NavLink} from "react-router-dom";


function nav() {


const isActive = {
fontWeight: "bold",
backgroundColor: "rgba(255, 255, 255, 0.1)",
};
    

return (
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/Shop" activeStyle={isActive}>
Shop
</NavLink>
</li>
</ul>
);


export default nav;


Use React Hooks.

import React, { useState } from 'react';


export const Table = () => {


const [activeMenu, setActiveMenu] = useState('transaction');


return(
<>
<Link className="flex-1 mr-2">
<a
id="transaction"
className={
activeMenu == 'transaction'
? 'text-center block border border-blue-500 rounded py-2 px-4 bg-blue-500 hover:bg-blue-700 text-white'
: 'text-center block border border-white rounded hover:border-gray-200 text-blue-500 hover:bg-gray-200 py-2 px-4'
}
href="#"
onClick={() => {
setActiveMenu('transaction');
}}>
Recent Transactions
</a>
</Link>
<Link className="flex-1 mr-2">
<a
id="account"
className={
activeMenu == 'account'
? 'text-center block border border-blue-500 rounded py-2 px-4 bg-blue-500 hover:bg-blue-700 text-white'
: 'text-center block border border-white rounded hover:border-gray-200 text-blue-500 hover:bg-gray-200 py-2 px-4'
}
href="#"
onClick={() => {
setActiveMenu('account');
}}>
Account Statement
</a>
</LInk>


</>
)
}

For me what worked has is using NavLink as it has this active class property.

  1. First import it

    import { NavLink } from 'react-router-dom';
    
  2. Use an activeClassName to get the active class property.

    <NavLink to="/" activeClassName="active">
    Home
    </NavLink>
    
    
    <NavLink to="/store" activeClassName="active">
    Store
    </NavLink>
    
    
    <NavLink to="/about" activeClassName="active">
    About Us
    </NavLink>
    
  3. Style your class in the css by the property active.

    .active{
    color:#fcfcfc;
    }
    
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand>
<Nav.Link as="{NavLink}" to="/">Brand Name</Nav.Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<nav className="me-auto">
<Nav.Link as="{NavLink}" to="/" exact>Home</Nav.Link>
<Nav.Link as="{NavLink}" to="/about">About</Nav.Link>
<Nav.Link as="{NavLink}" to="/contact">Contact</Nav.Link>
<NavDropdown title="Dropdown" id="basic-nav-dropdown">
<Nav.Link as="{NavLink}" to="/page1">Dropdown Link 1</Nav.Link>
<Nav.Link as="{NavLink}" to="/page2">Dropdown Link 2</Nav.Link>
<Nav.Link as="{NavLink}" to="/page3">Dropdown Link 3</Nav.Link>
</NavDropdown>
</nav>
</Navbar.Collapse>
</Container>

In react-router-dom documantion you use navlink instead of link. https://v5.reactrouter.com/web/api/NavLink

import { NavLink } from "react-router-dom";
<Nav>
<NavLink exact activeClassName="active--link" to="/" className="your class" > Home </NavLink>
<NavLink exact to="/classes" activeClassName="active--link" className="your class" > Classes </NavLink>
</Nav>
<NavLink to="/hello" activeClassName="active">Hello</NavLink>


React Router v6:

Source: Active NavLink Classes with React Router

You'll want to use the className property which now accepts a function and passes an isActive boolean property:

<NavLink
to="users"
className={({ isActive }) => (isActive ? 'active' : 'inactive')}
>
Users
</NavLink>

This is great for adding multiple classes too, and was a breaking change in v6 beta:

<NavLink
to="users"
className={({ isActive }) =>
isActive ? 'bg-green-500 font-bold' : 'bg-red-500 font-thin'
}
>
Users
</NavLink>

Read more and live demo: Active NavLink Classes with React Router

<NavLink to='/' activeClassName='active'>
Home
</NavLink>

I just used NavLink and activeClassName without wrapping it up in any component or something but my Code still works :)

In react router Version 6, we're doing it by NavLink and setting its className property.

We're specifying a function which will be called on every route change here.

   import { NavLink } from 'react-router-dom';
   

import classes from './MainHeader.module.css';
   

const MainHeader = () => {
return (
<header className={classes.header}>
<nav>
<ul>
<li>
<NavLink className={({isActive}) => isActive ? classes.active : ''} to='/welcome'>
Welcome
</NavLink>
</li>
<li>
<NavLink className={({isActive}) => isActive ? classes.active : ''} to='/products'>
Products
</NavLink>
</li>
</ul>
</nav>
</header>
);
};
   

export default MainHeader;


also, you can find a whole tutorial on it on this link

react-router-dom@v6

import { NavLink } from "react-router-dom";


<NavLink className = {isActive => isActive? 'active-css-class': 'general-css-class'}>link</NavLink>
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';


class NavBar extends Component {
render() {
return (
<div className="navbar">
<ul>
<li><NavLink to='/1' activeClassName="active">1</NavLink></li>
<li><NavLink to='/2' activeClassName="active">2</NavLink></li>
<li><NavLink to='/3' activeClassName="active">3</NavLink></li>
</ul>
</div>
);
}
}

this works for my...

We can use NavLink tag to highlight active link using isActive property

style:

const useStyles = makeStyles(theme => ({
active: {
borderBottom: '1px solid white',
color: 'yellow',
fontSize: '14px',
marginLeft: '16px',
textDecoration: 'none',
},}));

Component:

const classes = useStyles()
<NavLinks {...classes}/>
const NavLinks = classes => {
return (<>
<NavLink
to='/'
className={
useCallback(({isActive}) => isActive ? classes.active : classes.link)}
>
Home
</NavLink>
</>)
}

In - react material-ui, react-router-dom V5

<NavLink style={(isActive)=>({textDecoration: 'none', color: isActive ? '#1976d2':'inherit'})} to={`/`}>{`text`}</NavLink>

this way can apply the active link style