Home > Back-end >  React hover state & class added to all link menus instead to only the hovered one
React hover state & class added to all link menus instead to only the hovered one

Time:11-06

I'm trying to hover through each link & toggle the isHovered state on/off onMouseEnter & onMouseLeave which should toggle the class is-hovered on/off.

However, currently, the class is getting toggle in both links. How do I make sure it only changes in the hovered element?

This is my code:

import React from "react";
import "./nav.css";
import { Home } from "../pages/home";
import { Weather } from "../pages/weather";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

const routes = [
  {
    path: "/",
    exact: true,
    main: (props) => <Home {...props} />,
    label: "Home",
  },
  {
    path: "/weather",
    main: (props) => {
      return <Weather {...props} />;
    },
    label: "Weather",
  },
];

export class Nav extends React.Component {
  state = {
    isHovered: false,
  };

  mouseEnterHandler = () => {
    this.setState({
      isHovered: !this.state.isHovered,
    });
    console.log('I am in')
  }

  mouseLeaveHandler = () => {
    this.setState({
      isHovered: !this.state.isHovered,
    });
    console.log('I am out')
  }

  render() {
    return (
      <Router>
        <div className="container">
          <div className="sidebar_container">
            <ul className="sidebar">
              { console.log(this.state.isHovered) }
              { routes.map((route, i) => (
                <li key={i}>
                  <Link
                    className={this.state.isHovered ? "is-hovered" : ""}
                    to={route.path}
                    onMouseEnter={() => this.mouseEnterHandler()}
                    onm ouseLeave={() => this.mouseLeaveHandler()}
                  >
                    {route.label}
                  </Link>
                </li>
              ))}
            </ul>
          </div>
          <div className="page_container">
            <Switch>
              {routes.map((route, index) => (
                <Route
                  key={index}
                  path={route.path}
                  exact={route.exact}
                  children={<route.main apiKey={this.props.apiKey} />}
                />
              ))}
            </Switch>
          </div>
        </div>
      </Router>
    );
  }
}

CodePudding user response:

I have implemented your question using React Hooks

import "./styles.css";
import { useState } from "react";

export default function App() {
  const [hover, setHover] = useState(false);
  const [selectedId, setSelectedId] = useState(-1);

  const toggleHover = (state) => {
    setHover(state);
    console.log(hover);
  };

  const routes = [
    {
      id: 0,
      name: "Fname"
    },
    {
      id: 1,
      name: "Sname"
    }
  ];
  return (
    <div className="App">
      <ul>
        {routes.map((item, key) => {
          return (
            <li
              className={
                selectedId === item.id && hover === true ? "isHovered" : ""
              }
              onMouseEnter={() => {
                toggleHover(true);
                setSelectedId(item.id);
                console.log(selectedId);
              }}
              onm ouseLeave={() => {
                toggleHover(false);
                setSelectedId(-1);
                console.log(selectedId);
              }}
            >
              {item.name} {item.id}
            </li>
          );
        })}
      </ul>
    </div>
  );
}

You can use the same way using a class component.

This is the codesandbox to solution https://codesandbox.io/s/selectliitem-mxy7m

I hope this works for you!

CodePudding user response:

You need to create a wrapper component for Link to keep hover over information separately. The isHovered state being common was the issue.

I would create a component like below

import { useState } from "react";

const LinkWrapper = ({ route }) => {
  const [isHovered, setHovered] = useState(false);

  return (
    <Link
      className={isHovered ? "is-hovered" : ""}
      to={route.path}
      onMouseEnter={() => {
        setHovered(true);
      }}
      onm ouseLeave={() => {
        setHovered(false);
      }}
    >
      {route.label}
    </Link>
  );
};

export default LinkWrapper;

remove the following from the Nav component

  state = {
    isHovered: false,
  };

  mouseEnterHandler = () => {
    this.setState({
      isHovered: !this.state.isHovered,
    });
    console.log('I am in')
  }

  mouseLeaveHandler = () => {
    this.setState({
      isHovered: !this.state.isHovered,
    });
    console.log('I am out')
  }

and use in the Nav component like below

         <div className="sidebar_container">
            <ul className="sidebar">
              {routes.map((route, i) => (
                <li key={i}>
                  <LinkWrapper route={route} />
                </li>
              ))}
            </ul>
          </div>
  • Related