为什么我的 onClick 处理程序没有注销最新版本的状态,我该如何对此进行故障排除?



Sandbox

import React, { useEffect, useState } from "react";
export default function App() {
let [button, setButton] = useState(null);
let [num, setNum] = useState(5);
function revealState() {
console.log(num);
}
function changeState() {
setNum(Math.random());
}
useEffect(() => {
const el = (
<button id="logStateButton" onClick={revealState}>
Log state
</button>
);
setButton(el);
}, []);
return (
<>
{button}
<button onClick={changeState}>Change state</button>
</>
);
}

单击"日志状态按钮"可成功记录num状态。单击"更改状态按钮"已成功更改num状态。重新单击"日志状态按钮"不会记录更新的状态值 - 它会记录旧的状态值。

  1. 这是为什么呢?我的猜测是,由于useEffect只运行一次,它只引用第一个revealState函数,该函数只引用第一个num变量。因为它不在组件的return语句中,所以它不会"刷新"。

  2. 无论问题的原因是什么,有哪些解决方法?其中一些要求是:

  • 无法直接在return语句中呈现该标记。
  • 我们必须有那里的useEffect,并且它需要有某种 DEP 数组(每次重新执行函数组件时都不希望它触发)。
  • 在实际项目中,可能会对标签useEffect的回调渲染进行一些重要的更改 - 因此通过将类似num的东西放在其 dep 数组中来重新运行useEffect是不切实际的。

IMO,最简洁的解决方案是每次呈现页面时简单地添加更新的事件侦听器:

useEffect(() => {
el.onclick = onClickHandler
});

事件侦听器始终可以访问最新状态(和道具)。IMO,这个解决方案比前面提到的解决方案更具可扩展性 - 如果我的事件侦听器必须跟踪多个状态和道具的最新版本,这可能会变得混乱。有了这个,我需要做的就是在这个useEffect回调中添加额外的侦听器。思潮?

import React, { useEffect, useState, useCallback } from "react";
export default function App() {
let [button, setButton] = useState(null);
let [num, setNum] = useState(5);
const revealState = useCallback(() => {
console.log(num);
}, [num])
function changeState() {
setNum(Math.random());
}
useEffect(() => {
const el = (
<button id="logStateButton" onClick={revealState}>
Log state
</button>
);
setButton(el);
}, [revealState]);
return (
<>
{button}
<button onClick={changeState}>Change state</button>
</>
);
}

你可以听 revealState in useEffect. 它只有在使用 useCallback 实现的 num 被更改时才会初始化。 因此,每当您单击按钮时,num 都会更改,该 num 初始化 revealState 函数,而不是在其他重新渲染时初始化

您必须将num作为依赖项添加到useEffect

useEffect(() => {
const el = (
<button id="logStateButton" onClick={revealState}>
Log state
</button>
);
setButton(el);
}, [num]);

在对您的问题进行更多澄清之后,您似乎需要同时监视 num 和 HTML 状态。将Alan和kishore的代码组合在一起是解决方案。由于useEffect只观察Alan答案中的数字,因此如果对标签进行任何更改,则不会导致它重新运行。kishore还提到了另一个修复程序,即使用useCallback,但需要注意的是button而不是num。喜欢这个:

const updateButton = useCallback(function (newButton) {
setButton(newButton);
}, [button])
useEffect(() => {
const el = (
<button id="logStateButton" onClick={revealState}>
Log state
</button>
);
updateButton(el)
}, [num]);

这将告诉 useEffect 监视num,并且只有在状态更改时才会返回新buttonbutton

最新更新