[英]Graphql subscriptions inside a useEffect hook doesn't access latest state
I'm building a basic Slack clone.我正在构建一个基本的 Slack 克隆。 So I have a "Room", which has multiple "Channels".
所以我有一个“房间”,它有多个“频道”。 A user subscribes to all messages in a Room, but we only add them to the current message list if the new message is part of the user's current Channel
用户订阅了 Room 中的所有消息,但如果新消息是用户当前频道的一部分,我们只会将它们添加到当前消息列表中
const [currentChannel, setCurrentChannel] = useState(null);
const doSomething = (thing) => {
console.log(thing, currentChannel)
}
useEffect(() => {
// ... Here I have a call which will grab some data and set the currentChannel
Service.joinRoom(roomId).subscribe({
next: (x) => {
doSomething(x)
},
error: (err: any) => { console.log("error: ", err) }
})
}, [])
I'm only showing some of the code here to illustrate my issue.我只是在这里展示一些代码来说明我的问题。 The subscription gets created before
currentChannel
gets updated, which is fine, because we want to listen to everything, but then conditionally render based on currentChannel
.订阅在
currentChannel
更新之前创建,这很好,因为我们想听一切,然后根据currentChannel
有条件地呈现。
The issue I'm having, is that even though currentChannel
gets set correctly, because it was null when the next:
function was defined in the useEffect
hook, doSomething
will always log that currentChannel
is null.我遇到的问题是,即使
currentChannel
设置正确,因为在useEffect
钩子中定义next:
函数时它为空, doSomething
将始终记录currentChannel
为空。 I know it's getting set correctly because I'm displaying it on my screen in the render.我知道它设置正确,因为我在渲染的屏幕上显示它。 So why does
doSomething
get scoped in a way that currentChannel
is null?那么为什么
doSomething
以currentChannel
为空的方式获得范围? How can I get it to call a new function each time that accesses the freshest state of currentChannel
each time the next
function is called?我怎样才能得到每个访问的最新鲜的状态时它来调用一个新的函数
currentChannel
每个时间next
函数调用? I tried it with both useState
, as well as storing/retrieving it from redux, nothing is working.我尝试使用
useState
以及从 redux 存储/检索它,但没有任何效果。
Actually it is related to all async actions involving javascript closures: your subscribe
refers to initial doSomething
(it's recreated on each render) that refers to initial currentChannel
value.实际上,它与涉及 javascript 闭包的所有异步操作有关:您的
subscribe
指的是初始doSomething
(它在每次渲染时重新创建),它指的是初始currentChannel
值。 Article with good examples for reference: https://dmitripavlutin.com/react-hooks-stale-closures/文章有很好的例子供参考: https : //dmitripavlutin.com/react-hooks-stale-closures/
What can we do?我们可以做什么? I see at least 2 moves here: quick-n-dirty and fundamental.
我看到这里至少有两个动作:快速和基本。
useState
returns exact the same(referentially same) setter function each time and it allows us to use functional version:useState
返回完全相同(引用相同)的 setter 函数,它允许我们使用函数版本:const doSomething = (thing) => {
setCurrentChannel(currentChannelFromFunctionalSetter => {
console.log(thing, currentChannelFromFunctionalSetter);
return currentChannelFromFunctionalSetter;
}
}
useRef
and put most recent doSomething
there:useRef
并将最新的doSomething
放在那里:const latestDoSomething = useRef(null);
...
const doSomething = (thing) => { // nothing changed here
console.log(thing, currentChannel)
}
latestDoSomething.current = doSomething; // happens on each render
useEffect(() => {
Service.joinRoom(roomId).subscribe({
next: (x) => {
// we are using latest version with closure on most recent data
latestDoSomething.current(x)
},
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.