React.useEffect中通过两次异步查询订阅Firestore数据的实现问题咨询
实现方案
核心逻辑是在useEffect内部声明可变的取消订阅引用,异步执行订阅逻辑时给引用赋值真实的取消函数,清理函数直接判断引用有效性后执行即可,不会出现返回Promise的报错。
具体实现步骤
- 在
useEffect作用域内声明unsubscribe变量,初始值为undefined,用于存储后续生成的取消订阅函数 - 单独定义异步处理函数,依次完成用户所属客户查询、nodes查询创建、快照订阅的逻辑,订阅成功后将返回的取消函数赋值给之前声明的
unsubscribe变量 useEffect返回的清理函数判断unsubscribe是否为有效函数,是则执行完成资源释放- 将当前用户ID加入
useEffect依赖项,避免用户切换后订阅没有更新
完整TypeScript代码示例
import { useEffect, useState } from 'react'; import { doc, getDoc, query, collection, where, onSnapshot, Unsubscribe } from 'firebase/firestore'; import { db } from './path-to-your-firebase-init'; // 替换为你的firebase初始化文件路径 // 自定义Node类型,可根据实际业务属性修改 interface Node { id: string; [key: string]: any; } interface ComponentProps { currentUserId: string; // 传入当前登录用户的ID } const NodeListComponent: React.FC<ComponentProps> = ({ currentUserId }) => { const [nodes, setNodes] = useState<Node[]>([]); const [loading, setLoading] = useState<boolean>(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { let unsubscribe: Unsubscribe | undefined; const initSubscription = async () => { try { setLoading(true); // 1. 获取当前用户文档,拿到关联的customer引用 const userDocRef = doc(db, 'users', currentUserId); const userSnapshot = await getDoc(userDocRef); if (!userSnapshot.exists()) throw new Error('用户不存在'); const customerRef = userSnapshot.data().customer; if (!customerRef) throw new Error('用户未关联客户'); // 2. 构造对应客户的nodes查询 const nodesQuery = query( collection(db, 'nodes'), where('customer', '==', customerRef) ); // 3. 订阅快照,存储取消订阅函数 unsubscribe = onSnapshot(nodesQuery, (snapshot) => { const nodeList = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })) as Node[]; setNodes(nodeList); setLoading(false); }, (err) => { setError(err); setLoading(false); }); } catch (err) { setError(err as Error); setLoading(false); } }; initSubscription(); // 清理逻辑:组件卸载/依赖更新时取消订阅 return () => { if (unsubscribe) unsubscribe(); }; }, [currentUserId]); // 渲染逻辑可自行调整 if (loading) return <div>加载中...</div>; if (error) return <div>加载失败:{error.message}</div>; return ( <div className="node-list"> {nodes.map(node => ( <div key={node.id}> {/* 自定义node渲染内容 */} <p>Node ID: {node.id}</p> </div> ))} </div> ); }; export default NodeListComponent;
注意事项
该写法完全符合React useEffect的使用规范,不会出现异步返回导致的类型错误,同时内置了加载、错误状态的处理逻辑,可直接根据业务需求调整。如果不需要额外状态可以自行删除对应的state定义。
内容的提问来源于stack exchange,提问作者Albert
相关产品推荐
相关产品推荐

