I have a React App and I am trying to use the useState
and useEffect
hooks to create something like an animation when the user scrolled more than 200px from the top of the page.
This is how my navigation looks like:
And I want to apply
width: 0;
style property to the image in the middle when the user scrolls down (when he scrolled more than 200px from top), otherwise, when the user scrolls back to the top to "bring it back" on the page with width: 150px;
This is my Navbar
component:
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import logo from "../assets/images/logo.png";
import "../styles/navbar.scss";
const Navbar = () => {
const [scrollY, setScrollY] = useState(0);
const handleScroll = () => {
const logo = document.querySelector("#logo");
if (window.scrollY > 200) {
logo.style.width = "0";
} else {
logo.style.width = "150px";
}
setScrollY(window.scrollY);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return window.removeEventListener("scroll", handleScroll);
}, [scrollY]);
return (
<header>
<nav>
<Link to="/" className="nav-link">
Grupuri
</Link>
<Link to="/" className="nav-link">
Despre noi
</Link>
<Link to="/" className="nav-link">
Live
</Link>
</nav>
<img src={logo} alt="Maranata Lupeni" id="logo" />
<nav>
<Link to="/" className="nav-link">
Rugaciune
</Link>
<Link to="/" className="nav-link">
Credinta
</Link>
<Link to="/" className="nav-link">
Contact
</Link>
</nav>
</header>
);
};
export default Navbar;
How can I solve this?
You have multiple issues that you need to address for your code to work:
useEffect
(() => {
). The function would be executed when the useEffect
is called again (dependencies changed) or if the component unmounts. You returned the result of calling removeEventListener
. Calling removeEventListener
removed the event listener just after adding it.className
or style
properties.useEffect
should be executed only once. Define handleScroll
inside the useEffect
, and don't pass any redundant states (like scrollY
).scrollY
, just if you need to show or hide the element.Example:
const { useState, useEffect } = React;
const Demo = () => {
const [hideLogo, setHideLogo] = useState(false);
useEffect(() => {
const handleScroll = () => {
const logo = document.querySelector("#logo");
setHideLogo(window.scrollY > 50);
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div
className="logo"
style={{ width: hideLogo ? 0 : '150px' }} />
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
.logo {
height: 100px;
margin: 0 auto;
background: lightblue;
transition: width 0.3s;
}
body {
height: 300vh;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>
Notes: