将 useEffect 与事件侦听器一起使用

IT技术 javascript reactjs
2021-05-16 01:02:49

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

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

简单的例子:

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 代码片段由于某种原因不起作用。

在此处输入图片说明

3个回答

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

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

例如

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

我认为正确的解决方案应该是:codesanbox我们实际上是在说要注意它的依赖项,也就是回调。每当它改变时,我们应该在闭包中使用正确的值进行另一个绑定。

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

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

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

  const handleClick = useCallback(() => {
    setNameFromEventHandler(name)
  }, [name])

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

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