Это потому, что вы определили useEffect()
без каких-либо зависимостей , поэтому ваш useEffect()
будет запускаться только один раз и никогда не вызывает handleNavigation()
при y
изменениях . Чтобы исправить это, вам нужно добавить y
в свой массив зависимостей, чтобы ваш useEffect()
запускался после изменения значения y
. Затем вам нужно еще одно изменение, чтобы вступить в силу в вашем коде, где вы пытаетесь инициализировать свой y
с помощью window.scrollY
, поэтому вы должны либо сделать это в своем useState()
, например:
const [y, setY] = useState(window.scrollY);
useEffect(() => {
window.addEventListener("scroll", (e) => handleNavigation(e));
return () => { // return a cleanup function to unregister our function since its gonna run multiple times
window.removeEventListener("scroll", (e) => handleNavigation(e));
};
}, [y]);
Если по какой-то причине окно там может быть недоступно или вы не хотите делать это здесь, вы можете сделать это двумя отдельными useEffect()
s.
Итак, ваши useEffect()
s должны быть такими:
useEffect(() => {
setY(window.scrollY);
}, []);
useEffect(() => {
window.addEventListener("scroll", (e) => handleNavigation(e));
return () => { // return a cleanup function to unregister our function since its gonna run multiple times
window.removeEventListener("scroll", (e) => handleNavigation(e));
};
}, [y]);
ОБНОВЛЕНИЕ
После самостоятельной реализации этого решения. Я обнаружил, что к этому решению следует применить некоторые примечания. Итак, , поскольку handleNavigation()
изменит значение y
напрямую, мы можем игнорировать y
как нашу зависимость, а затем добавить handleNavigation()
как зависимость к нашему useEffect()
, тогда из-за этого изменения мы должен оптимизировать handleNavigation()
, поэтому мы должны использовать для него useCallback()
. Тогда окончательный результат будет примерно таким:
const handleNavigation = useCallback(
e => {
const window = e.currentTarget;
if (y > window.scrollY) {
console.log("scrolling up");
} else if (y < window.scrollY) {
console.log("scrolling down");
}
setY(window.scrollY);
}, [y]
);
useEffect(() => {
setY(window.scrollY);
window.addEventListener("scroll", e => handleNavigation(e));
return () => {
window.removeEventListener("scroll", e => handleNavigation(e));
};
}, [handleNavigation]);
Я тестирую это снова, но все еще сталкиваюсь с той же проблемой , что и раньше, и при попытке прокрутки вверх он сломается. и будет утешать некоторую прокрутку вниз в событии прокрутки вверх.
Итак, я пришел к выводу, что мемоизация в этом случае нам совсем не поможет. Наконец, я обнаружил, что подход, который мы здесь используем, не очень хорошо работает, потому что нам следует подумать о более эффективном способе сохранения последней позиции прокрутки страницы каждый раз, когда мы хотим проверить новую позицию. Кроме того, чтобы избавиться от огромного количества утешительных прокрутки вверх и прокрутки вниз , мы должны определить порог для запуска изменения события прокрутки. Так что я просто немного поискал в Интернете и получил это gist , которое было очень полезно. Затем, вдохновившись этим, я реализую более простую версию.
Вот как это выглядит:
const [scrollDir, setScrollDir] = useState("scrolling down");
useEffect(() => {
const threshold = 0;
let lastScrollY = window.pageYOffset;
let ticking = false;
const updateScrollDir = () => {
const scrollY = window.pageYOffset;
if (Math.abs(scrollY - lastScrollY) < threshold) {
ticking = false;
return;
}
setScrollDir(scrollY > lastScrollY ? "scrolling down" : "scrolling up");
lastScrollY = scrollY > 0 ? scrollY : 0;
ticking = false;
};
const onScroll = () => {
if (!ticking) {
window.requestAnimationFrame(updateScrollDir);
ticking = true;
}
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
Как это работает?
Я просто go сверху вниз и объясните каждый блок кода.
Итак, я просто определил пороговую точку с начальным значением 0
, а затем всякий раз, когда прокрутка идет вверх или вниз, она будет новый расчет, вы можете увеличить его, если не хотите сразу рассчитывать новое смещение страницы.
Затем вместо scrollY
я решаю использовать pageYOffset
, что более надежно в пересечении просмотр.
В функции updateScrollDir
мы просто проверим, соблюдается ли порог, затем, если он соблюден, я укажу базу направления прокрутки текущей и предыдущей страницы смещение.
Наиболее важной его частью является функция onScroll
. Мы просто использовали requestAnimationFrame
, чтобы убедиться, что мы вычисляем новое смещение после того, как страница была полностью отрисована после прокрутки. А затем с флагом ticking
мы убедимся, что мы просто запускаем обратный вызов нашего прослушивателя событий один раз в каждом requestAnimationFrame
.
Наконец, мы определили наш слушатель и нашу функцию очистки .
Тогда состояние scrollDir
будет содержать фактическое направление прокрутки.
Рабочая демонстрация:
CodeSandbox