Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add active class to clicked item in ReactJS

I have an array of objects that contain keypair values for the navbar links, I have mapped the array to list and rendering it successful, but when I try to add class to only active link, it is adding the class to all the link items.

Wondering where I am going wrong?

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

class SideNavbar extends Component {
  constructor(props) {
    super(props);
    this.state = [
      { id: 1, name: "Link1", to: "/cms", className: "side_nav_item" },
      { id: 2, name: "Link2", to: "/cms", className: "side_nav_item" },
      { id: 3, name: "Link3", to: "/cms", className: "side_nav_item" },
      { id: 4, name: "Link4", to: "/cms", className: "side_nav_item" }
    ];
  }

  handleClick = () => {
    const currentClass = document.getElementsByClassName("side_nav_item");
    for (let i = 0; i < currentClass.length; i++) {
      currentClass[i].classList.toggle("active_item");
      console.log(currentClass[i]);
    }
  };
  render() {
    const navLinks = this.state.map(link => {
      return (
        <div key={link.id}>
          <ul>
            <li onClick={this.handleClick} className={link.className}>
              <Link to={link.to}>{link.name}</Link>
            </li>
          </ul>
        </div>
      );
    });
    return <div>{navLinks}</div>;
  }
}

export default SideNavbar;
like image 924
Tayyab Rahman Avatar asked Apr 04 '19 14:04

Tayyab Rahman


2 Answers

Instead of trying to manipulate the DOM manually, you could keep a piece of state called e.g. activeLink that stores the id of the active link that you use that to apply the class to the active link in the render method.

Example

class SideNavbar extends React.Component {
  state = {
    links: [
      {
        id: 1,
        name: "Link1",
        to: "/cms",
        className: "side_nav_item"
      },
      {
        id: 2,
        name: "Link2",
        to: "/cms",
        className: "side_nav_item"
      },
      {
        id: 3,
        name: "Link3",
        to: "/cms",
        className: "side_nav_item"
      },
      {
        id: 4,
        name: "Link4",
        to: "/cms",
        className: "side_nav_item"
      }
    ],
    activeLink: null
  };

  handleClick = id => {
    this.setState({ activeLink: id });
  };

  render() {
    const { links, activeLink } = this.state;

    return (
      <div>
        {links.map(link => {
          return (
            <div key={link.id}>
              <ul>
                <li
                  onClick={() => this.handleClick(link.id)}
                  className={
                    link.className +
                    (link.id === activeLink ? " active_item" : "")
                  }
                >
                  {link.name} {link.id === activeLink && "active!"}
                </li>
              </ul>
            </div>
          );
        })}
      </div>
    );
  }
}

ReactDOM.render(<SideNavbar />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>
like image 68
Tholle Avatar answered Oct 27 '22 13:10

Tholle


The issue is this line of code:
const currentClass = document.getElementsByClassName('side_nav_item');

is grabbing all your HTML elements with the side_nav_items CSS class and in your case this is all your side nav links. You probably want to use:

let element = document.getElementById(id);

to just get the side nav element which was clicked identified by it's id. Then you can use an element.classList.add('active_item'); to add the active_item css class to the selected side nav item.

Hopefully that helps!

like image 1
Nathan Avatar answered Oct 27 '22 12:10

Nathan