useEffect挂钩中的Graphql订阅无法访问最新状态

时间:2019-12-22 07:14:05

标签: reactjs apollo react-apollo apollo-client

我正在构建一个基本的Slack克隆。因此,我有一个“房间”,其中有多个“通道”。用户订阅了会议室中的所有消息,但是只有当新消息属于用户当前频道的一部分时,我们才会将它们添加到当前消息列表中。

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的设置正确,因为在next:钩子{{1}中定义了useEffect函数时,它为空}始终记录doSomething为空。我知道它的设置正确,因为我正在渲染器的屏幕上显示它。那么,为什么以currentChannel为空的方式限制doSomething的范围呢?每次调用currentChannel函数时,如何访问每次访问currentChannel的最新状态的新函数?我同时尝试了next和从redux存储/检索它,都没有任何效果。

1 个答案:

答案 0 :(得分:2)

实际上,它与所有涉及javascript关闭的异步操作有关:您的subscribe引用了初始doSomething的初始currentChannel(在每个渲染器上重新创建)。带有示例的文章供参考:https://dmitripavlutin.com/react-hooks-stale-closures/

我们该怎么办?我在这里至少看到两个动作:快速n脏和基本动作。

  1. 我们可以利用useState每次都返回完全相同(参考相同)的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) 
    },