currently I am making a navbar that only shows when you scroll up, to prevent useEffect to run everytime when the visible state get changed, I had to use both a ref and a state that is synced together to do comparison in the useEffect, using ref and a state to keep track of a same value seems extremely fishy, is there another way of doing this? one that does not involve triggering useEffect from creating the event handlers everytime the state changes?
import React, { useState, useRef, useEffect } from 'react';
import Link from 'next/link';
const NavbarLink = ({ name, href }: { name: string, href: string }) => {
return (
<Link href={href}>
<a>{ name }</a>
</Link>
);
}
const Navbar = () => {
const scrollYRef = useRef(0);
const visibleRef = useRef(true);
const [visible, setVisible] = useState(true);
useEffect(() => {
const onScroll = (event: Event) => {
event.preventDefault();
if ((window.scrollY < scrollYRef.current) != visibleRef.current) {
visibleRef.current = !visibleRef.current;
setVisible(x => !x);
}
scrollYRef.current = window.scrollY;
}
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
}
}, []);
return (
<div className={`${!visible && '-translate-y-full'} fixed flex w-full h-32 font-bold text-white transition-all`}>
<NavbarLink name="home" href='/'/>
</div>
);
}
You could use the "latest ref" pattern (here via react-use
's useLatest
hook ), which boxes the latest value of a state atom at each component update time, so you don't need to manage it manually, and you don't need it as a dependency for the hook. (I'd also grab useEvent
from react-use
if you end up using it.)
import React, { useState, useRef, useEffect } from "react";
import Link from "next/link";
// via https://github.com/streamich/react-use/blob/master/src/useLatest.ts
const useLatest = (value) => {
const ref = useRef(value);
ref.current = value;
return ref;
};
const NavbarLink = ({ name, href }: { name: string; href: string }) => {
return (
<Link href={href}>
<a>{name}</a>
</Link>
);
};
function useVisibleOnScrollUp(initial = true) {
const scrollYRef = useRef(window.scrollY);
const [visible, setVisible] = useState(initial);
const latestVisibleRef = useLatest(visible);
useEffect(() => {
const onScroll = (event: Event) => {
event.preventDefault();
const currentVisible = latestVisibleRef.current;
if (window.scrollY < scrollYRef.current != currentVisible) {
setVisible((x) => !x);
}
scrollYRef.current = window.scrollY;
};
window.addEventListener("scroll", onScroll);
return () => {
window.removeEventListener("scroll", onScroll);
};
}, []);
return visible;
}
const Navbar = () => {
const visible = useVisibleOnScrollUp(true);
return (
<div
className={`${
!visible && "-translate-y-full"
} fixed flex w-full h-32 font-bold text-white transition-all`}
>
<NavbarLink name="home" href="/" />
</div>
);
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.