繁体   English   中英

如何在useEffect中使用useState?

[英]How to use useState in useEffect?

我尝试将我的组件从 react.component 重构为钩子,但我遇到了一些麻烦。 我真的不明白如何使用我的 state 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]);

手柄滚动 function:

function handleScroll() {
    console.log(offsetTop);
    const scrolledY = window.scrollY
    if (scrolledY > offsetTop) console.log("works")
}

与第First try:放置在componentDidMount() {}中并使用this可以正常工作。 所以不好的不是条件。 这是关于何时设置值以及useStateuseEffect如何工作。

您的条件在第一个变体中不起作用吗? 那个看起来是正确的,在这种情况下, 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]);

您第二次尝试的问题是它执行以下操作:

  • 第一次渲染:
    • 第一个 useEffect 设置新的offsetTop
    • 第二个 useEffect 用旧的offsetTop触发
  • offsetTop更新导致重新渲染
    • 第二个 useEffect 进行清理,删除事件侦听器
    • 我猜,浏览器在某处传播事件,而你的听众不见了
    • 2nd useEffect 第二次触发,使用新的offsetTop ,并再次设置你的监听器(但它已经错过了事件)

啊...这只是您问题的一部分。

问题的第二部分是handleScroll()有效地从创建它并设置为侦听器的渲染周期中捕获offsetTop值。 您应该使用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]);

这样, offsetTop在重新渲染中是持久的,并且handleScroll()始终使用当前值。 如果您还需要在offsetTop更新上重新渲染,那么您还可以使用useState()来触发重新渲染(比如也将offsetTop值注入到渲染的组件中。

这让我觉得你可能根本不需要 state。 为什么不根据需要导出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]);

转载: https://codesandbox.io/s/so-67247616-pl455

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM