React Native+Firebase实时数据库双人Bingo游戏异步问题及等待页求助
问题解决与实现方案
一、setGame 返回 undefined 的原因及修复
核心问题
原 setGame 函数未返回 Promise,导致使用 await 时无法获取有效结果。同时函数内部存在异步操作未正确等待、遍历逻辑无法中断等问题。
修复后的代码
1. 重构 create 函数(返回游戏密钥)
export function create(game) { const db = getDatabase(); const newGameKey = push(child(ref(db), 'GAME')).key; return set(ref(db, '/GAME/' + newGameKey), game) .then(() => { console.log('Game Create!'); return newGameKey; // 返回新创建的游戏密钥,用于后续监听 }) .catch((error) => { console.log(error); throw error; // 抛出错误便于上层捕获 }); }
2. 重构 setGame 函数(返回 Promise 及游戏状态)
export async function setGame(email, game) { const dbRef = ref(getDatabase()); const snapshot = await get(child(dbRef, 'GAME/')); if (snapshot.exists()) { // 使用 for...of 遍历,可随时中断循环找到第一个可用游戏 for (const childSnapshot of snapshot.children()) { const key = childSnapshot.key; const { player1, player2 } = childSnapshot.val(); if (player2 === "" && email !== player1) { await updateGame(email, key); return { type: 'UPDATE', gameKey: key }; // 标记为加入已有游戏 } } // 无可用游戏,创建新游戏 const gameKey = await create(game); return { type: 'CREATE', gameKey: gameKey }; } else { // 首次创建游戏 const gameKey = await create(game); return { type: 'CREATE', gameKey: gameKey }; } }
3. 确保 updateGame 返回 Promise(已正确实现)
export function updateGame(email, key) { console.log('Update: ' + key); const db = getDatabase(); const updates = {}; updates[`/GAME/${key}/player2`] = email; return update(ref(db), updates); }
二、等待页面实现方案
核心思路
通过状态控制条件渲染,结合 Firebase 实时监听功能,在玩家创建游戏后持续监听房间状态,直到第二名玩家加入。
修复后的 Game 组件
function Game() { const authCtx = useContext(AuthContext); const gameCtx = useContext(GameContext); const [isWaiting, setIsWaiting] = useState(false); const [gameKey, setGameKey] = useState(null); const [isLoading, setIsLoading] = useState(true); // 新增初始化加载状态 useEffect(() => { let unsubscribe = null; async function gamePlay() { try { // 建议:若 GameContext 使用 useState/reducer,应通过 setter 更新 player1 gameCtx.player1 = authCtx.token; const result = await setGame(authCtx.token, gameCtx); setGameKey(result.gameKey); if (result.type === 'CREATE') { setIsWaiting(true); // 监听游戏房间,等待第二名玩家加入 const db = getDatabase(); const gameRef = ref(db, `/GAME/${result.gameKey}`); unsubscribe = onValue(gameRef, (snapshot) => { const gameData = snapshot.val(); if (gameData.player2 && gameData.player2 !== "") { setIsWaiting(false); gameCtx.player2 = gameData.player2; // 更新上下文的第二名玩家信息 } }); } else if (result.type === 'UPDATE') { setIsWaiting(false); // 获取已有游戏的玩家1信息 const db = getDatabase(); const gameRef = ref(db, `/GAME/${result.gameKey}`); const snapshot = await get(gameRef); gameCtx.player1 = snapshot.val().player1; } } catch (error) { console.error('游戏初始化失败:', error); // 可添加错误提示逻辑 } finally { setIsLoading(false); } } gamePlay(); // 组件卸载时清理监听 return () => { if (unsubscribe) unsubscribe(); }; }, []); // 初始化加载状态 if (isLoading) { return <LoadingOverlay message="Initializing game..." />; } // 等待第二名玩家状态 if (isWaiting) { return <LoadingOverlay message="Waiting for another player... " />; } // 游戏开始,渲染卡片 return <Card gameKey={gameKey} />; } export default Game;
关键改进点
- 条件渲染:通过
isWaiting状态控制等待页面与游戏卡片的切换,避免在useEffect中返回 JSX 的错误做法。 - 实时监听:创建游戏后,通过 Firebase 的
onValue监听房间状态,一旦检测到第二名玩家加入,自动切换到游戏界面。 - 内存泄漏防护:在组件卸载时清理 Firebase 监听,避免内存泄漏。
- 错误处理:添加
try/catch捕获初始化过程中的错误,提升稳定性。
内容的提问来源于stack exchange,提问作者user19225784
相关产品推荐
相关产品推荐

