在滚动事件上侦听的函数中未更新 React 钩子

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

handleScroll正如console.log所示,该功能已正确收听。然而,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);

更新


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


白衣染霜花
浏览 273回答 3
3回答

慕沐林林

为了从useEffect回调内部监听 state 的变化(当你没有关注任何 props 更新时),你可以将 state 保存在组件范围之外的变量中,并直接使用它而不是 state。代码如下:import React, { useState, useEffect } from "react";import ReactDOM from "react-dom";const debounce = (func, wait, immediate) => {&nbsp; let timeout;&nbsp; return function() {&nbsp; &nbsp; const context = this;&nbsp; &nbsp; const args = arguments;&nbsp; &nbsp; clearTimeout(timeout);&nbsp; &nbsp; timeout = setTimeout(() => {&nbsp; &nbsp; &nbsp; timeout = null;&nbsp; &nbsp; &nbsp; if (!immediate) func.apply(context, args);&nbsp; &nbsp; }, wait);&nbsp; &nbsp; if (immediate && !timeout) func.apply(context, args);&nbsp; };};let isFetchingState;const App = () => {&nbsp; const [isFetching, setIsFetching] = useState(false);&nbsp; isFetchingState = isFetching;&nbsp; const handleScroll = debounce(() => {&nbsp; &nbsp; setIsFetching(!isFetchingState);&nbsp; &nbsp; console.log({ isFetchingState });&nbsp; }, 300);&nbsp; useEffect(() => {&nbsp; &nbsp; window.addEventListener("scroll", handleScroll);&nbsp; &nbsp; return () => window.removeEventListener("scroll", handleScroll);&nbsp; }, []);&nbsp; return <div style={{ height: "1280px" }}>Hello world</div>;};const root = document.getElementById("root");if (root) ReactDOM.render(<App />, root);

守着一只汪

添加isFetching为依赖项useEffect虽然我无法提供深入的解释,但我可以说,useEffect当您说效果不依赖于任何东西时,您基本上是在向 React 撒谎,通过提供一个空的依赖项数组,传递包含在您中的所有变量总是好的影响。此外,您每次组件时都会创建一个新函数re-render,以避免将函数移动到内部useEffect或将其包装在内部useCallback,除非依赖项数组中的某些内容发生更改,否则不会创建重新创建函数useEffect(&nbsp; () => {&nbsp; &nbsp; const handleScroll = debounce(() => {&nbsp; &nbsp; &nbsp; setIsFetching(prevState => !prevState);&nbsp; &nbsp; &nbsp; console.log({ isFetching });&nbsp; &nbsp; }, 300);&nbsp; &nbsp; window.addEventListener("scroll", handleScroll);&nbsp; &nbsp; return () => window.removeEventListener("scroll", handleScroll);&nbsp; },&nbsp; [isFetching]);或与 useCallbackconst handleScroll = useCallback(&nbsp; debounce(() => {&nbsp; &nbsp; setIsFetching(prevState => !prevState);&nbsp; &nbsp; console.log({ isFetching });&nbsp; }, 300),&nbsp; [isFetching]);useEffect(&nbsp; () => {&nbsp; &nbsp; window.addEventListener("scroll", handleScroll);&nbsp; &nbsp; return () => window.removeEventListener("scroll", handleScroll);&nbsp; },&nbsp; [isFetching]);

拉莫斯之舞

您已将一个空数组作为第二个参数传递给 useEffect。这就是您在运行后不再被称为 useEffect 的关键问题。要解决此问题,请不要传递第二个参数。&nbsp;useEffect(() => {&nbsp; &nbsp; window.addEventListener("scroll", handleScroll);&nbsp; &nbsp; return () => window.removeEventListener("scroll", handleScroll);&nbsp; });或者,每当属性被更改时调用 useEffect :useEffect(() => {&nbsp; &nbsp; window.addEventListener("scroll", handleScroll);&nbsp; &nbsp; return () => window.removeEventListener("scroll", handleScroll);&nbsp; }, [isFetching]); // re-run useEffect on isFetching changed这类似于我们在 componentDidUpdate 中所做的:if (prevState.count !== this.state.count) {&nbsp; // do the stuff有关更多详细信息,请参阅文档本身。文档中的注释:如果您只想运行效果并仅将其清理一次(在装载和卸载时),您可以传递一个空数组 ([]) 作为第二个参数。这告诉 React 你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。这不是作为特殊情况处理的——它直接遵循依赖项数组的工作方式。如果传递一个空数组 ([]),则效果中的 props 和 state 将始终具有其初始值。虽然传递 [] 作为第二个参数更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型,但通常有更好的解决方案来避免过于频繁地重新运行效果。另外,不要忘记 React 将 useEffect 的运行推迟到浏览器绘制完成之后,所以做额外的工作不是问题。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript