【问题标题】:Using useEffect with event listeners将 useEffect 与事件侦听器一起使用
【发布时间】:2019-03-20 14:03:00
【问题描述】:

我遇到的问题是,当我设置事件侦听器时,事件侦听器看到的值不会随状态更新。就好像它绑定到初始状态一样。

这样做的正确方法是什么?

简单示例:

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

const App = () => {
  const [name, setName] = useState("Colin");
  const [nameFromEventHandler, setNameFromEventHandler] = useState("");

  useEffect(() => {
    document.getElementById("name").addEventListener("click", handleClick);
  }, []);

  const handleButton = () => {
    setName("Ricardo");
  };

  const handleClick = () => {
    setNameFromEventHandler(name);
  };

  return (
    <React.Fragment>
      <h1 id="name">name: {name}</h1>
      <h2>name when clicked: {nameFromEventHandler}</h2>
      <button onClick={handleButton}>change name</button>
    </React.Fragment>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

下面的 Gif,因为 SO 代码 sn-p 由于某种原因不起作用。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    所以您的问题是您将一个空数组作为第二个参数传递给您的效果,因此效果将永远不会被清理并再次触发。这意味着handleClick 只会在默认状态下关闭。在此组件的整个生命周期中,您基本上都在写:setNameFromEventHandler("Colin");

    尝试同时删除第二个参数,以便在状态更改时清理并触发效果。当效果重新触发时,将处理单击事件的函数将在您的状态的最新版本上关闭。此外,从您的 useEffect 返回一个函数,该函数将删除您的事件侦听器。

    例如

      useEffect(() => {
        document.getElementById("name").addEventListener("click", handleClick);
        return () => {
          document.getElementById("name").removeEventListener("click", handleClick);
        }
      });
    

    【讨论】:

    • 在这个例子中传递[name]作为第二个参数是安全的
    • 没错,这也可能有助于更好地解释发生了什么 - 每当名称更改时,删除旧的事件侦听器并附加一个新的。
    • 这是有道理的。我还认为使用[name] 会更好一些,因为在现实世界中,我不想在每次任何状态更改时都附加/分离侦听器。
    • 这也导致我修复了我的真实解决方案,所以我将其标记为正确。谢谢!
    【解决方案2】:

    我认为正确的解决方案应该是:codesanbox。我们告诉效果要注意它的依赖关系,即回调。每当它发生变化时,我们都应该在闭包中使用正确的值进行另一个绑定。

    【讨论】:

      【解决方案3】:

      我相信正确的解决方案是这样的:

        useEffect(() => {
          document.getElementById("name").addEventListener("click", handleClick);
        }, [handleClick]);
      
        const handleButton = () => {
          setName("Ricardo");
        };
      
        const handleClick = useCallback(() => {
          setNameFromEventHandler(name)
        }, [name])
      

      useEffect 应该将 handleClick 作为其依赖数组的一部分,否则它将遭受所谓的“陈旧闭包”,即具有陈旧状态。

      为确保 useEffect 不在每次渲染上运行,请将 handleClick 移动到 useCallback 中。这将返回回调的记忆版本,仅当依赖项之一发生更改时才会更改,在本例中为“名称”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-08-17
        • 1970-01-01
        • 1970-01-01
        • 2019-12-30
        • 1970-01-01
        • 1970-01-01
        • 2021-03-10
        相关资源
        最近更新 更多