在滚动事件上侦听的函数中未更新反应挂钩

Nic*_*Diz 9 javascript events reactjs react-hooks

我有一个handleScroll在滚动事件上监听的函数。此函数必须更新isFetching(以false开头,并且必须更改布尔值)。

如图所示,该函数handleScroll已正确收听console.log。但是,isFetching总是错误的。似乎setIsFetching从未读过。我认为,另一个选择就像eventListener冻结了handleScroll函数的第一个版本。

我该怎么做才能更新该函数中的钩子? 这是代码和codesandbox的简化版本:

/* <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);
Run Code Online (Sandbox Code Playgroud)

更新

我将一个空数组作为第二个参数,useEffect因为我希望第一个参数函数仅在componentDidMount()上触发一次

Asa*_*viv 5

添加isFetching为依赖项useEffect

虽然我无法提供深入的解释,但我可以说,当您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]
);
Run Code Online (Sandbox Code Playgroud)

或者与useCallback

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

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

使用效果的完整指南


小智 2

为了从回调内部监听状态的变化useEffect(当您不跟踪任何 props 更新时),您可以将状态保存在组件范围之外的变量中,并直接使用它而不是状态。

这里有代码:

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);
Run Code Online (Sandbox Code Playgroud)