我尝试将我的组件从 react.component 重构为 hooks,但我遇到了一些麻烦。我真的不明白如何使用我的状态 offsetTop
。该值已设置,或在我需要时不设置。
第一次尝试:
const [offsetTop, setOffsetTop] = useState();
useEffect(() => {
setOffsetTop(window.pageYOffset + document.querySelector(".projectsContainer").getBoundingClientRect().top)
console.log(offsetTop);
window.addEventListener('scroll', handleScroll, true)
return () => window.removeEventListener("scroll", handleScroll);
}, []);
我还尝试将它与第二种方法 useEffect
一起使用,[offsetTop]
作为第二个参数,但我的日志显示了两次 undef 和值。我明白为什么它显示了两次,但仍然没有解决我的问题,我的条件不起作用。
第二次尝试:
const [offsetTop, setOffsetTop] = useState();
useEffect(() => {
setOffsetTop(window.pageYOffset + document.querySelector(".projectsContainer").getBoundingClientRect().top)
}, []);
useEffect(() => {
console.log(offsetTop);
window.addEventListener('scroll', handleScroll, true)
return () => window.removeEventListener("scroll", handleScroll);
}, [offsetTop]);
handleScroll 函数:
function handleScroll() {
console.log(offsetTop);
const scrolledY = window.scrollY
if (scrolledY > offsetTop) console.log("works")
}
与放置在 First try:
中的 componentDidMount() {}
相同的代码并使用 this
工作正常。所以这不是坏的条件。这是关于何时设置值以及 useState
和 useEffect
如何工作。
答案 0 :(得分:4)
您的条件在第一个变体中不起作用吗?那个看起来是正确的,在这种情况下 console.log(offsetTop)
不应该打印新设置的值,而是之前设置的值。要像您需要的那样记录它:
const [offsetTop, setOffsetTop] = useState();
useEffect(() => {
setOffsetTop(window.pageYOffset + document.querySelector(".projectsContainer").getBoundingClientRect().top)
window.addEventListener('scroll', handleScroll, true)
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
console.log(offsetTop);
}, [offsetTop]);
第二次尝试的问题在于它执行以下操作:
offsetTop
offsetTop
触发offsetTop
更新导致重新渲染
offsetTop
第二次触发,并再次设置您的侦听器(但它已经错过了该事件)啊……那只是你问题的一部分。
问题的第二部分是 handleScroll()
有效地从创建它并设置为侦听器的渲染周期中捕获 offsetTop
值。您应该使用 useRef()
使 handleScroll()
与当前值保持同步(如果您出于其他原因不需要 offsetTop
,则根本不需要使用状态. 你可以这样做:
const { current: heap } = useRef({
offsetTop: 0,
});
useEffect(() => {
function handleScroll() {
console.log(heap.offsetTop);
const scrolledY = window.scrollY
if(scrolledY > heap.offsetTop) console.log("works")
}
heap.offsetTop = window.pageYOffset + document.querySelector(".projectsContainer").getBoundingClientRect().top;
window.addEventListener('scroll', handleScroll, true)
return () => window.removeEventListener("scroll", handleScroll);
// The heap is guaranteed to be the same object across re-renders,
// thus including it into dependencies below does not cause useEffect
// to recompute. However, if you don't include it, and rely on ESLint
// to check code errors, it will complain about heap not being there
// as missing dependency.
}, [heap]);
这样,offsetTop
在重新渲染时是持久的,而 handleScroll()
始终使用当前值。如果您还需要在 offsetTop
更新时重新渲染,那么您还可以使用 useState()
来触发重新渲染(比如还将 offsetTop
值注入到渲染组件中。
答案 1 :(得分:2)
这让我觉得你可能根本不需要状态。为什么不根据需要推导出 offsetTop
的值?
useEffect(() => {
function handleScroll() {
const scrolledY = window.scrollY;
const offsetTop = window.pageYOffset + document.querySelector(".projectsContainer").getBoundingClientRect().top;
if(scrolledY > offsetTop) console.log("works")
}
window.addEventListener('scroll', handleScroll, true)
return () => window.removeEventListener("scroll", handleScroll, true);
}, []);
然而,这个想法可能是您想要捕获 offsetTop
的初始值,然后在情况发生变化时使用它。在这种情况下,我认为您的主要问题是由于 removeEventListener
没有被正确清理,因为您没有传递与提供给 addEventListener
的参数相同的参数(缺少 true
)。因此,仍然有一个 handleScroll
实例保存了第一次渲染时 offsetTop
的初始值,我认为这就是您看到 undefined
被记录的原因。
useEffect(() => {
setOffsetTop(window.pageYOffset +
document.querySelector(".projectsContainer").getBoundingClientRect().top;);
}, []);
useEffect(() => {
// The first time this useEffect runs, the value of `offsetTop`
// is still the initial value of undefined.
function handleScroll() {
console.log(offsetTop)
const scrolledY = window.scrollY;
if (scrolledY > offsetTop) console.log("works");
}
window.addEventListener("scroll", handleScroll, true);
// This doesn't get removed correctly
// because you don't pass the same parameters (missing `true`)
return () => window.removeEventListener("scroll", handleScroll);
}, [offsetTop]);
useEffect(() => {
setOffsetTop(window.pageYOffset +
document.querySelector(".projectsContainer").getBoundingClientRect().top;);
}, []);
useEffect(() => {
function handleScroll() {
const scrolledY = window.scrollY;
if (scrolledY > offsetTop) console.log("works");
}
window.addEventListener("scroll", handleScroll, true);
return () => window.removeEventListener("scroll", handleScroll, true);
}, [offsetTop]);