簡體   English   中英

在滾動事件中偵聽的函數中未更新的React鈎子

[英]React hook not updated in function listened on scroll event

我有一個在scroll事件上監聽的函數handleScroll Thsi函數必須更新isFetching (它開始為false並且必須更改布爾值)。

正如console.log所示,正確監聽函數handleScroll 但是, isFetching總是錯誤的。 似乎從未讀過setIsFetching 我認為另一種選擇就像eventListener凍結了handleScroll函數的第一個版本。

我該怎么做才能更新該函數中的鈎子? 這是代碼和代碼的簡化版本:

/* <div id='root'></div> */
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const debounce = (func, wait, immediate) => {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      timeout = null;
      if (!immediate) func.apply(context, args);
    }, wait);
    if (immediate && !timeout) func.apply(context, args);
  };
};
const App = () => {
  const [isFetching, setIsFetching] = useState(false);
  const handleScroll = debounce(() => {
    setIsFetching(!isFetching);
    console.log({ isFetching });
  }, 300);
  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);
  return <div style={{ height: "1280px" }}>Hello world</div>;
};
const root = document.getElementById("root");
if (root) ReactDOM.render(<App />, root);

UPDATE

我把一個空數組作為useEffect的第二個參數,因為我希望第一個param函數只在componentDidMount()上觸發一次

為了從useEffect回調內部監聽狀態的變化(當你沒有跟蹤任何道具更新時),你可以將狀態保存在組件范圍之外的變量中,並直接使用它而不是狀態。

在這里你有代碼:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const debounce = (func, wait, immediate) => {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      timeout = null;
      if (!immediate) func.apply(context, args);
    }, wait);
    if (immediate && !timeout) func.apply(context, args);
  };
};

let isFetchingState;

const App = () => {
  const [isFetching, setIsFetching] = useState(false);

  isFetchingState = isFetching;

  const handleScroll = debounce(() => {
    setIsFetching(!isFetchingState);
    console.log({ isFetchingState });
  }, 300);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  return <div style={{ height: "1280px" }}>Hello world</div>;
};

const root = document.getElementById("root");

if (root) ReactDOM.render(<App />, root);

添加isFetching作為isFetching的依賴useEffect

雖然我無法提供深入的解釋,但我可以說當你說效果不依賴於任何東西時,你基本上在React中useEffect了React,通過提供一個空的依賴數組,總是很好地傳遞包含在中的所有變量你的影響。

此外,每次re-render組件時都會創建一個新函數,以避免在useEffect移動函數或將其包裝在useCallback ,而不會創建重新創建函數,除非依賴項數組中的某些內容發生更改

useEffect(
  () => {
    const handleScroll = debounce(() => {
      setIsFetching(prevState => !prevState);
      console.log({ isFetching });
    }, 300);
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  },
  [isFetching]
);

或者使用useCallback

const handleScroll = useCallback(
  debounce(() => {
    setIsFetching(prevState => !prevState);
    console.log({ isFetching });
  }, 300),
  [isFetching]
);

useEffect(
  () => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  },
  [isFetching]
);

useEffect的完整指南

您已將空數組作為useEffect的第二個參數傳遞。 這就是你在運行后不再被稱為useEffect的關鍵問題。

要解決這個問題,請不要傳遞第二個參數。

 useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  });

或者,每當要更改的屬性時調用useEffect:

useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [isFetching]); // re-run useEffect on isFetching changed

這與我們在componentDidUpdate中的操作類似:

if (prevState.count !== this.state.count) {
  // do the stuff

有關更多詳細信息,請參閱文檔本身。

來自文檔的說明:

如果要運行效果並僅將其清理一次(在裝載和卸載時),則可以將空數組([])作為第二個參數傳遞。 這告訴React你的效果不依賴於來自props或state的任何值,所以它永遠不需要重新運行。 這不作為特殊情況處理 - 它直接遵循依賴項數組的工作方式。

如果傳遞一個空數組([]),效果中的props和state將始終具有其初始值。 雖然傳遞[]作為第二個參數更接近熟悉的componentDidMount和componentWillUnmount心理模型,但通常有更好的解決方案來避免經常重新運行效果。 此外,不要忘記React推遲運行useEffect直到瀏覽器繪制完成后,所以做額外的工作不是問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM