useEffect 挂钩内的 Graphql 订阅不会访问最新状态
Graphql subscriptions inside a useEffect hook doesn't access latest state
我正在构建一个基本的 Slack 克隆。所以我有一个 "Room",它有多个 "Channels"。一个用户订阅了一个房间里的所有消息,但是如果新消息是用户当前频道的一部分,我们只会将它们添加到当前消息列表
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) }
})
}, [])
我在这里只展示了一些代码来说明我的问题。订阅在 currentChannel
更新之前创建,这很好,因为我们想听所有内容,然后根据 currentChannel
.
有条件地呈现
我遇到的问题是,即使 currentChannel
设置正确,因为在 useEffect
挂钩中定义 next:
函数时它为空,doSomething
将始终记录 currentChannel
为空。我知道它设置正确,因为我在渲染中的屏幕上显示它。那么,为什么 doSomething
会以 currentChannel
为空的方式进行限定?每次调用 next
函数时,如何让它每次访问 currentChannel
的最新状态时调用一个新函数?我用 useState
以及来自 redux 的 storing/retrieving 都试过了,没有任何效果。
实际上它与涉及 javascript 闭包的所有异步操作有关:您的 subscribe
指的是初始 doSomething
(它在每次渲染时重新创建)指的是初始 currentChannel
价值。文章有很好的例子供参考:https://dmitripavlutin.com/react-hooks-stale-closures/
我们能做什么?我在这里看到至少 2 个动作:quick-n-dirty 和 fundamental。
- 我们每次都可以利用
useState
returns 完全相同(引用相同)setter 函数,它允许我们使用功能版本:
const doSomething = (thing) => {
setCurrentChannel(currentChannelFromFunctionalSetter => {
console.log(thing, currentChannelFromFunctionalSetter);
return currentChannelFromFunctionalSetter;
}
}
- 基本方法是利用
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)
},
我正在构建一个基本的 Slack 克隆。所以我有一个 "Room",它有多个 "Channels"。一个用户订阅了一个房间里的所有消息,但是如果新消息是用户当前频道的一部分,我们只会将它们添加到当前消息列表
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) }
})
}, [])
我在这里只展示了一些代码来说明我的问题。订阅在 currentChannel
更新之前创建,这很好,因为我们想听所有内容,然后根据 currentChannel
.
我遇到的问题是,即使 currentChannel
设置正确,因为在 useEffect
挂钩中定义 next:
函数时它为空,doSomething
将始终记录 currentChannel
为空。我知道它设置正确,因为我在渲染中的屏幕上显示它。那么,为什么 doSomething
会以 currentChannel
为空的方式进行限定?每次调用 next
函数时,如何让它每次访问 currentChannel
的最新状态时调用一个新函数?我用 useState
以及来自 redux 的 storing/retrieving 都试过了,没有任何效果。
实际上它与涉及 javascript 闭包的所有异步操作有关:您的 subscribe
指的是初始 doSomething
(它在每次渲染时重新创建)指的是初始 currentChannel
价值。文章有很好的例子供参考:https://dmitripavlutin.com/react-hooks-stale-closures/
我们能做什么?我在这里看到至少 2 个动作:quick-n-dirty 和 fundamental。
- 我们每次都可以利用
useState
returns 完全相同(引用相同)setter 函数,它允许我们使用功能版本:
const doSomething = (thing) => {
setCurrentChannel(currentChannelFromFunctionalSetter => {
console.log(thing, currentChannelFromFunctionalSetter);
return currentChannelFromFunctionalSetter;
}
}
- 基本方法是利用
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)
},