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。

  1. 我们每次都可以利用 useState returns 完全相同(引用相同)setter 函数,它允许我们使用功能版本:
const doSomething = (thing) => {
  setCurrentChannel(currentChannelFromFunctionalSetter => {
    console.log(thing, currentChannelFromFunctionalSetter);
    return currentChannelFromFunctionalSetter;
  }
}
  1. 基本方法是利用 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) 
    },