skip to Main Content

I have a Navigation Panel with link to different pages inside the react app. I want to highlight the currently navigation link. I am able to achieve this by adding a class to active nav link. My implementation is as below:

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

function Nav() {

    const highlight = (e) => {
        // Remove .nav-select class from all nav items
        let eles = document.querySelectorAll(".nav-select");
        eles.forEach.call(eles, (ele) => {
            ele.classList.remove('nav-select');
        })
        // Add .nav-select to currently clicked nav item
        e.target.className = e.target.className + " nav-select"
    }

    return (
        <nav className="main-nav">
            <ul>
                <li onClick={highlight}>
                    <Link to="/" >Home</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/blog" >Blog</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/clock" >Clock</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/video" >Video</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/goals" >Goals</Link>
                </li>
            </ul>
        </nav>
    );
};

export default Nav;

Related styling:

.main-nav .nav-select {
  border: 1px solid red;
  padding: 2px
}

This is giving me the expected result.

As per my understanding, a call to document and it’s modification is a side-effect, and it is good practice to handle side-effects inside useEffect() hook. Is it just a good practice or is it a rule that might lead to issues if not followed?
Is there a better implementation or a way to improve my implementaion, to achieve my expected result?

2

Answers


  1. Use NavLink instead of Link, and use the isActive prop to control the class:

    <li>
      <NavLink to="/" 
        style={({ isActive }) => 
          className={({ isActive }) =>
            isActive ? 'nav-select' : undefined
          } 
        }>Home
      </NavLink>
    </li>
    
    Login or Signup to reply.
  2. In this case, using <NavLink> is the recommended way but the root problem you’re having is not thinking "the React way". This means you should avoid direct manipulation of the DOM, such as document.querySelectorAll and directly modifying DOM elements to add/remove classes and such.

    The react way is to store state and have the DOM render according to this state. When you want to change something on an element, update your state and the DOM will be re-rendered to reflect the changes.

    For example, without using <NavLink>, you could solve the problem like so:

    Codesandbox demo

    const App = () => {
      const [links, setLinks] = useState([
        { text: "Home", to: "/", active: false },
        { text: "About", to: "/about", active: false },
        { text: "Topics", to: "/topics", active: false }
      ]);
    
      const handleClick = (clickedLink) => {
        setLinks(links.map((link) => ({ ...link, active: clickedLink === link })));
      };
    
      return (
        <Router>
            <ul>
              {links.map((link, index) => (
                <li key={index}>
                  <Link
                    onClick={() => handleClick(link)}
                    className={link.active ? "nav-active" : ""}
                    to={link.to}
                  >
                    {link.text}
                  </Link>
                </li>
              ))}
            </ul>
        </Router>
      );
    };
    

    This works by storing an array of objects which represent the link in state, and to render these using .map(). There is a click event which passes the clicked link and then updates the state to set the active property to true for the clicked link and false for all others. When the link is rendered this active property is used to set the nav-active class or not.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search