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
Use
NavLink
instead ofLink
, and use theisActive
prop to control the class: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 asdocument.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
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 theactive
property totrue
for the clicked link andfalse
for all others. When the link is rendered thisactive
property is used to set thenav-active
class or not.