我做了一个简单的计数器示例,想要在鼠标单击时控制台日志值,但它总是显示 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;
问题是,每当您的组件重新渲染自身(由于状态更改)时,都会为
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;
最后一项之所以有效,有两个原因:
handler
正在使用对
num
最新版本的引用,并且该引用将始终保持最新,因为我们在
setNum
回调中更新它。这也意味着
handler
在渲染之间不会改变。
num
发生变化时,不必删除事件处理程序,这会导致 console.log 滞后于 num 的值。
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>