[英]How to use useState in useEffect?
I try to refacto my compoenent from react.component to hooks but I got some troubles.我尝试将我的组件从 react.component 重构为钩子,但我遇到了一些麻烦。 I don't really understand how to use my state
offsetTop
.我真的不明白如何使用我的 state
offsetTop
。 The value is set, or not when I need.该值已设置,或者在我需要时不设置。
First try:第一次尝试:
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);
}, []);
I also tried to use it with a second method useEffect
with [offsetTop]
as a second parameters but my log showed up twice with undef and the value.我还尝试将它与第二种方法
useEffect
一起使用,并将[offsetTop]
作为第二个参数,但我的日志显示了两次 undef 和值。 I understand why it shows twice, but still doesn't solve my problem, my condition does not work.我明白为什么它显示两次,但仍然没有解决我的问题,我的情况不起作用。
Second try:第二次尝试:
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:手柄滚动 function:
function handleScroll() {
console.log(offsetTop);
const scrolledY = window.scrollY
if (scrolledY > offsetTop) console.log("works")
}
The same code as the First try:
placed in componentDidMount() {}
and using this
works fine.与第
First try:
放置在componentDidMount() {}
中并使用this
可以正常工作。 So it is not the condition that is bad.所以不好的不是条件。 It's about when the value is set and how
useState
and useEffect
works.这是关于何时设置值以及
useState
和useEffect
如何工作。
Doesn't your condition work in the first variant?您的条件在第一个变体中不起作用吗? That one looks correct, and
console.log(offsetTop)
in that case is not supposed to print the newly set value, but the previously set value.那个看起来是正确的,在这种情况下,
console.log(offsetTop)
不应该打印新设置的值,而是打印之前设置的值。 To log it like you want you need:要按照您的需要记录它:
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]);
The problem with your second try is that it does the following:您第二次尝试的问题是它执行以下操作:
offsetTop
offsetTop
offsetTop
offsetTop
触发offsetTop
update causes re-render offsetTop
更新导致重新渲染
offsetTop
, and sets your listener again (but it already missed the event) offsetTop
,并再次设置你的监听器(但它已经错过了事件) Ah... that's only a part of your problem.啊...这只是您问题的一部分。
The second part of your problem is that handleScroll()
effectively captures offsetTop
value from the render cycle where it was created and set as the listener.问题的第二部分是
handleScroll()
有效地从创建它并设置为侦听器的渲染周期中捕获offsetTop
值。 You should use useRef()
to keep handleScroll()
in-sync with the current value (and if you don't need offsetTop
for other reasons, you don't need to use the state at all. You can do:您应该使用
useRef()
使handleScroll()
与当前值保持同步(如果您因为其他原因不需要offsetTop
,则根本不需要使用 state。您可以这样做:
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]);
This way, offsetTop
is persistent across re-renders, and handleScroll()
always uses the current value.这样,
offsetTop
在重新渲染中是持久的,并且handleScroll()
始终使用当前值。 And if you need also re-render on offsetTop
updates, then you can additionally use useState()
to trigger re-renders (say to also inject offsetTop
values into rendered components.如果您还需要在
offsetTop
更新上重新渲染,那么您还可以使用useState()
来触发重新渲染(比如也将offsetTop
值注入到渲染的组件中。
This strikes me as a case where you may not need state at all.这让我觉得你可能根本不需要 state。 Why not derive the value of
offsetTop
as needed?为什么不根据需要导出
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);
}, []);
However, maybe the idea is that you want to capture the initial value of offsetTop
and then use it as things change.但是,也许这个想法是您想要捕获
offsetTop
的初始值,然后在事情发生变化时使用它。 In that case, I think your main problem is due to removeEventListener
not being cleaned up correctly because you're not passing the same parameters as those given to addEventListener
(missing true
).在这种情况下,我认为您的主要问题是由于
removeEventListener
没有被正确清理,因为您没有传递与addEventListener
相同的参数(缺少true
)。 Because of that, there is still an instance of handleScroll
holding the initial value of offsetTop
from the first render, and I think that's why you're seeing undefined
being logged.正因为如此,仍然有一个
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]);
Reproduction: https://codesandbox.io/s/so-67247616-pl455转载: https://codesandbox.io/s/so-67247616-pl455
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.