为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

白衣染霜花 2021-06-30 13:43:38
我有一个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() 上触发一次
查看完整描述

3 回答

?
慕沐林林

TA贡献2016条经验 获得超9个赞

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


代码如下:


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);


查看完整回答
反对 回复 2021-07-08
?
守着一只汪

TA贡献1872条经验 获得超3个赞

添加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]

);

或与 useCallback


const handleScroll = useCallback(

  debounce(() => {

    setIsFetching(prevState => !prevState);

    console.log({ isFetching });

  }, 300),

  [isFetching]

);


useEffect(

  () => {

    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll);

  },

  [isFetching]

);


查看完整回答
反对 回复 2021-07-08
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

您已将一个空数组作为第二个参数传递给 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 你的 effect 不依赖于来自 props 或 state 的任何值,所以它永远不需要重新运行。这不是作为特殊情况处理的——它直接遵循依赖项数组的工作方式。


如果传递一个空数组 ([]),则效果中的 props 和 state 将始终具有其初始值。虽然传递 [] 作为第二个参数更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型,但通常有更好的解决方案来避免过于频繁地重新运行效果。另外,不要忘记 React 将 useEffect 的运行推迟到浏览器绘制完成之后,所以做额外的工作不是问题。


查看完整回答
反对 回复 2021-07-08
  • 3 回答
  • 0 关注
  • 272 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信