zil*_*nas 20 javascript addeventlistener reactjs react-hooks
首先,对于类组件,这可以正常工作并且不会导致任何问题。
但是,在带有钩子的功能组件中,每当我尝试从滚动事件侦听器的函数设置状态时,handleScroll即使我使用了去抖动,我的状态也无法更新或应用程序的性能会受到严重影响。
import React, { useState, useEffect } from "react";
import debounce from "debounce";
let prevScrollY = 0;
const App = () => {
const [goingUp, setGoingUp] = useState(false);
const handleScroll = () => {
const currentScrollY = window.scrollY;
if (prevScrollY < currentScrollY && goingUp) {
debounce(() => {
setGoingUp(false);
}, 1000);
}
if (prevScrollY > currentScrollY && !goingUp) {
debounce(() => {
setGoingUp(true);
}, 1000);
}
prevScrollY = currentScrollY;
console.log(goingUp, currentScrollY);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div>
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
</div>
);
};
export default App;
Run Code Online (Sandbox Code Playgroud)
试图useCallback在handleScroll函数中使用钩子,但没有多大帮助。
我究竟做错了什么?如何在handleScroll不对性能产生巨大影响的情况下设置状态?
我已经用这个问题创建了一个沙箱。
Yoz*_*ozi 17
在您的代码中,我看到了几个问题:
1) []in useEffect 意味着它不会看到任何状态变化,比如goingUp. 它总是会看到初始值goingUp
2)debounce行不通。它返回一个新的去抖动函数。
3)通常全局变量是一种反模式,认为它只适用于您的情况。
4)你的滚动监听器不是被动的,正如@skyboyer 所提到的。
import React, { useState, useEffect, useRef } from "react";
const App = () => {
const prevScrollY = useRef(0);
const [goingUp, setGoingUp] = useState(false);
useEffect(() => {
const handleScroll = () => {
const currentScrollY = window.scrollY;
if (prevScrollY.current < currentScrollY && goingUp) {
setGoingUp(false);
}
if (prevScrollY.current > currentScrollY && !goingUp) {
setGoingUp(true);
}
prevScrollY.current = currentScrollY;
console.log(goingUp, currentScrollY);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, [goingUp]);
return (
<div>
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
<div style={{ background: "orange", height: 100, margin: 10 }} />
</div>
);
};
export default App;
Run Code Online (Sandbox Code Playgroud)
https://codesandbox.io/s/react-setstate-from-event-listener-q7to8
小智 6
总之,你需要添加
goingUp为useEffect的依赖。
如果您使用[],您将只创建/删除带有函数的侦听器(handleScroll在初始渲染中创建)。换句话说,重新渲染时,滚动事件侦听器仍在使用handleScroll初始渲染中的旧内容。
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [goingUp]);
Run Code Online (Sandbox Code Playgroud)
使用自定义钩子
我建议将整个逻辑移动到自定义钩子中,这可以使您的代码更清晰,更易于重用。我useRef用来存储以前的值。
export function useScrollDirection() {
const prevScrollY = useRef(0)
const [goingUp, setGoingUp] = useState(false);
const handleScroll = () => {
const currentScrollY = window.scrollY;
if (prevScrollY.current < currentScrollY && goingUp) {
setGoingUp(false);
}
if (prevScrollY.current > currentScrollY && !goingUp) {
setGoingUp(true);
}
prevScrollY.current = currentScrollY;
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [goingUp]);
return goingUp ? 'up' : 'down';
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
22418 次 |
| 最近记录: |