使用addEventListener和useState时如何修复react中的闭包

问题描述 投票:0回答:2

我做了一个简单的计数器示例,想要在鼠标单击时控制台日志值,但它总是显示 0。如何修复它?

import "./App.css";
import { useState } from "react";
import { useEffect } from "react";

function App() {
  const [num, setNum] = useState(0);

  const handler = () => {
    console.log(num);
  };

  useEffect(() => {
    window.addEventListener("click", handler);
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <div
          onClick={() => {
            setNum(num + 1);
          }}
        >
          Add
        </div>
        <h1>{num}</h1>
      </header>
    </div>
  );
}

export default App;

image

javascript reactjs
2个回答
2
投票

问题是,每当您的组件重新渲染自身(由于状态更改)时,都会为

num
设置一个新值,并且
handler
创建一个新函数。

但是,

useEffect
内的事件处理程序永远不会看到为
handler
创建的新函数,因此它将继续调用旧函数,该函数仍然保留对
num
旧值(为零)的引用。

这里有一个快速修复方法,可以让您的代码开始工作:

import "./App.css";
import { useEffect, useState, useCallback } from "react";


function App() {
  const [num, setNum] = useState(0);

  const handler = useCallback(() => {
    console.log(num);
  }, [num]);

  useEffect(() => {
    window.addEventListener("click", handler);
    return () => {
      window.removeEventListener("click", handler);
    };
  }, [handler]);

  return (
    <div className="App">
      <header className="App-header">
        <div
          onClick={() => {
            setNum(num + 1);
          }}
        >
          Add
        </div>
        <h1>{num}</h1>
      </header>
    </div>
  );
}

export default App;

当您运行此命令时,您将看到控制台中打印的值落后于 DOM 中显示的控制台中打印的值。我会让你找出原因。

如果您确实想使用事件侦听器,而不是按照其他答案的建议进行操作,您可以尝试以下操作:

import "./App.css"; import { useEffect, useState, useRef, useCallback } from "react"; function App() { const [num, setNum] = useState(0); const numRef = useRef(num); const handler = useCallback(() => { console.log(numRef.current); }, []); useEffect(() => { window.addEventListener("click", handler); return () => { window.removeEventListener("click", handler); }; }, [handler]); return ( <div className="App"> <header className="App-header"> <div onClick={() => { setNum(num => { numRef.current = num + 1; return numRef.current; }) }} > Add </div> <h1>{num}</h1> </header> </div> ); } export default App;
最后一项之所以有效,有两个原因:

  1. handler
    正在使用对
    num
    最新版本的引用,并且该引用将始终保持最新,因为我们在
    setNum
    回调中更新它。这也意味着 
    handler
     在渲染之间不会改变。
  2. 每次
  3. num
     发生变化时,不必删除事件处理程序,这会导致 console.log 滞后于 num 的值。

0
投票
您应该使用

useEffect

 检查 
num
 状态的变化,而不是添加不必要的 
addEventListener

const { useEffect, useState } = React; function Example() { const [ num, setNum ] = useState(0); function handler() { setNum(num + 1); } useEffect(() => console.log(num), [num]); return ( <div className="App"> <header className="App-header"> <div onClick={handler}>Add</div> <h1>{num}</h1> </header> </div> ); } ReactDOM.render( <Example />, document.getElementById('react') );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

© www.soinside.com 2019 - 2024. All rights reserved.