我正在构建一个基本的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存储/检索它,都没有任何效果。
答案 0 :(得分:2)
实际上,它与所有涉及javascript关闭的异步操作有关:您的subscribe
引用了初始doSomething
的初始currentChannel
(在每个渲染器上重新创建)。带有示例的文章供参考:https://dmitripavlutin.com/react-hooks-stale-closures/
我们该怎么办?我在这里至少看到两个动作:快速n脏和基本动作。
useState
每次都返回完全相同(参考相同)的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)
},