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>