App.jsx:
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'
import Leaderboard from './pages/Leaderboard'
import About from './pages/About'
import Scoresfeed from './pages/Scores-feed'
import Header from './components/Header/Header'
import './App.css'
const App = () => {
return (
<Router>
<Header/>
<Routes>
<Route path="/about" element={<About />} />
<Route path="/scores-feed" element={<Scoresfeed />} />
<Route path="/" element={<Navigate to="/leaderboard" />} />
<Route path="/leaderboard" element={<Leaderboard />} />
</Routes>
</Router>
);
};
export default App;
Header.jsx:
import { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import './Header.css';
const Header = () => {
const [isNavOpen, setIsNavOpen] = useState(false);
const navRef = useRef(null);
useEffect(() => {
const handleOutsideClick = (event) => {
if (navRef.current && !navRef.current.contains(event.target)) {
setIsNavOpen(false);
}
};
document.addEventListener('click', handleOutsideClick);
return () => {
document.removeEventListener('click', handleOutsideClick);
};
}, [isNavOpen]);
const handleNavToggle = () => {
setIsNavOpen((isNavOpen) => !isNavOpen);
};
return (
<header className="header">
<div className="nav-toggle" onClick={handleNavToggle}>
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={`feather feather-chevron-down ${isNavOpen ? 'open' : ''}`}
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
<span className={`nav-toggle-label ${isNavOpen ? 'hidden' : ''}`}>Open navigation</span>
</div>
<nav ref={navRef} className={`nav ${isNavOpen ? 'open' : ''}`}>
<ul className="nav-list">
<li className="nav-item">
<Link to="/leaderboard">
<svg
width="140"
height="50"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 50"
preserveAspectRatio="xMidYMid meet"
className="svg-icon"
>
<rect x="5" y="40" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<rect x="180" y="5" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="1" y1="12" x2="12" y2="1" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="179" y1="49" x2="190" y2="37" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<polygon points="20,0 190,0 190,30 172,50 0,50 0,40 0,20" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="3" className="svg-border" />
<text className='text' x="50%" y="50%" textAnchor="middle" fill="white" dy=".3em" fontSize="30">Leaderboard</text>
</svg>
</Link>
</li>
<li className="nav-item">
<Link to="/scores-feed">
<svg
width="140"
height="50"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 50"
preserveAspectRatio="xMidYMid meet"
className="svg-icon"
>
<rect x="5" y="40" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<rect x="180" y="5" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="1" y1="12" x2="12" y2="1" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="179" y1="49" x2="190" y2="37" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<polygon points="20,0 190,0 190,30 172,50 0,50 0,40 0,20" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="3" className="svg-border" />
<text className='text' x="50%" y="50%" textAnchor="middle" fill="white" dy=".3em" fontSize="30">Scores feed</text>
</svg>
</Link>
</li>
<li className="nav-item">
<Link to="/about">
<svg
width="140"
height="50"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 50"
preserveAspectRatio="xMidYMid meet"
className="svg-icon"
>
<rect x="5" y="40" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<rect x="180" y="5" width="5" height="5" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="1" y1="12" x2="12" y2="1" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<line x1="179" y1="49" x2="190" y2="37" stroke="rgb(19, 65, 150)" strokeWidth="2" />
<polygon points="20,0 190,0 190,30 172,50 0,50 0,40 0,20" fill="none" stroke="rgb(19, 65, 150)" strokeWidth="3" className="svg-border" />
<text className='text' x="50%" y="50%" textAnchor="middle" fill="white" dy=".3em" fontSize="30">About</text>
</svg>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default Header;
I have a component that needs to render a header. Inside it there is a svg “button” that should be toggled, thereby changing the state and adding/removing classes so that the menu is shown. But for some reason this doesn’t happen.
But if I change the state directly through React DevTools, then the menu appears:
I tried changing the dependencies in useEffect but it didn’t help. Classes are simply not added, and the state does not change. Please tell me what is the reason for my mistake
2
Answers
Аfter 12 hours I solved the problem like this:
that looks like you’re going to be stacking eventlisteners anytime isNavOpen is updated you’ll add another listener, probably just removing isNavOpen from the dependency array could help, you only want to bind the eventlistener once on mount