我有以下(简化的)react本地代码。请阅读下面的代码以获得解释。
const [id, setId] = useState(null);
const functionOne = (id) => {
setId(id);
};
const functionTwo = (newId) => {
if (id === newId) {
console.log('ids are the same');
} else {
console.log('ids are not the same');
}
};
useEffect(() => {
const socket = new WebSocket(url);
socket.addEventListener('open', () => {
console.log('socket connection opened');
});
socket.addEventListener('message', (msg) => {
const data = JSON.parse(msg.data);
if (data.eventType === 'one') {
functionOne(data.id);
}
if (data.eventType === 'two') {
functionTwo(data.id);
}
})
socket.addEventListener('close', () => {
console.log('socket connection closed');
});
}, []);
字符串
我有一个useState
-hook作为ID。我有两个事件通过websockets从服务器发送到应用程序:事件“one”和事件“two”。
在事件“一”中:我向应用程序发送一个唯一的ID,并将此ID存储在useState-id-hook中。这很有效,因为当我在useState-function中执行console.log以查看状态是否更改时,它可以工作。
1分钟后,我收到了另一个事件类型为“two”的套接字消息,它的ID与我一分钟前收到的ID完全相同。我想将这个“新”ID与之前收到的ID进行比较。
问题是:当运行functionTwo()时,似乎useState-hook仍然停留在原始状态,例如“null”。它确实通过此更新得到更新,但未反映在functionTwo()中,其中“id”的值仍然是“null”。
在useCallback(functionTwo,[id])中 Package functionTwo()不起作用。
我的代码有什么问题?
编辑:
感谢T. J.克劳德,使用Redux的建议在我的情况下起作用。
我创建了一个自定义钩子,它模仿了useState钩子,但将值存储在redux中。
1条答案
按热度按时间2ic8powd1#
这是经典的function-closes-over-stale-state问题,通过
functionTwo
使其间接存在,* 稍微 * 模糊了一点。问题是你的
useEffect
回调函数 * 关闭了 * 第一次渲染的functionTwo
版本,这反过来又关闭了 * 第一次渲染的id
的值。后来的id
和functionTwo
版本没有被使用,因为你的useEffect
依赖数组是空的。(一个好的lint插件会警告你这一点。)有几种方法可以解决它:
1.将
functionTwo
作为useEffect
的依赖项添加,并确保添加适当的清理代码(在从useEffect
回调返回的清理函数中),这样就不会重新订阅套接字(因此在重新订阅时取消订阅)。字符串
请注意,这意味着效果会在 * 每个渲染 * 上运行。您可以通过添加您为
functionTwo
提到的useCallback
来减少它的发生频率,因此functionTwo
对于任何给定的id
值都是稳定的,因此(反过来)只有当id
发生变化时才会产生效果。1.添加具有最新版本
id
的ref
,并在functionTwo
中使用该ref
,而不是直接使用id
。型
这样一来,
functionTwo
可能是过时的就没关系了。1.使用Redux或类似的东西来代替
useState
。(一个例子超出了这个答案的范围。)