簡體   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