React使用Firebase onSnapshot监听消息时useState取值为null如何解决
问题原因
这是React的闭包陷阱导致的:你定义的useEffect依赖项只有chat,仅会在组件初始化和chat值变化时执行一次。执行时effect内部捕获的是当前作用域下的messages初始值null,后续onSnapshot的回调函数一直引用的是这个闭包中的旧messages值,所以打印结果始终为null。setMessages功能正常是因为React的状态更新本身是队列化处理的,不受闭包旧值影响。
解决方案
优先使用setState的函数式更新语法,函数式更新的入参永远是最新的状态值,不需要依赖外部作用域的状态变量,修改后代码如下:
const [messages, setMessages] = useState(null); const [chat, setChat] = useState(props.chatId); useEffect(() => { const q = query(collection(db, "messages"), where("chat_id", "==", chat), orderBy("date","desc"), limit(5)); // 创建数据库监听器 const unsuscribe = onSnapshot(q, (querySnapshot) => { setMessages(prevMessages => { console.log(prevMessages); // 此处可正常获取最新的消息列表状态 if(prevMessages === null){ console.log("首次设置消息列表"); return querySnapshot.docs; }else{ console.log("更新消息列表"); return [...querySnapshot.docs, ...prevMessages]; } }) }); return () => { console.log("取消监听"); unsuscribe(); } }, [chat]);
注意不要把
messages加入useEffect的依赖数组,否则每次messages更新都会销毁重建监听器,会产生额外的性能开销和重复请求问题。
内容的提问来源于stack exchange,提问作者Martin Pichler
相关产品推荐
相关产品推荐

