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

时间:2019-03-20 14:03:01

标签: javascript reactjs

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

正确的方法是什么?

简单的示例:

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代码段无效。

enter image description here

3 个答案:

答案 0 :(得分:3)

因此,您的问题是您将空数组作为效果的第二个参数传递,因此效果永远不会被清除并再次触发。这意味着handleClick只会在默认状态下关闭。您实际上已经写了:setNameFromEventHandler("Colin");,涵盖了该组件的整个生命周期。

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

例如

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

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

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

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

要确保useEffect不在每个渲染上运行,请将handleClick移到useCallback内。这将返回一个已记忆的回调版本,仅当其中一个依赖项已更改(在这种情况下为“名称”)更改时,该更改才会更改。