React搭配Firebase数据库如何正确设置与访问数据对象?
问题根因
- 直接用Firebase返回的
snapshot.val()覆盖playerData默认值:如果非先锋玩家的返回数据没有settlement字段,覆盖后playerData.settlement会变为undefined,后续访问settlement.settlementName直接触发空值报错 - 异步逻辑时序错误:
pioneer_emails.on('value')不是Promise类型,await不生效,CheckForPioneer里的if(!pioneer)判断取的是初始false值,逻辑和预期不符;且GetPlayerData和CheckForPioneer并行执行,可能出现pioneer状态已更新为true,但playerData还未拉取到settlement字段的时序差问题 - 嵌套字段判断不全:渲染时仅判断了
playerData存在,未校验中间层player_data/settlement字段是否存在
修复方案
1. 数据拉取时保留默认结构
修改GetPlayerData里的set逻辑,避免全量覆盖默认结构,也可以直接用可选链做兜底:
// 方案1:合并默认结构和返回数据,缺失字段保留默认值 if (snapshot.exists()) { const remoteData = snapshot.val(); setPlayerData({ player_data: {playerName: "", ...remoteData.player_data}, settlement: {settlementName: "", ...remoteData.settlement}, }); } // 方案2:更简单的取值兜底,不用改set逻辑,直接在变量定义时用可选链+空值合并 const playerName = playerData?.player_data?.playerName ?? ""; const settlementName = playerData?.settlement?.settlementName ?? "";
2. 修复先锋校验的异步逻辑
将持续监听的on改为一次性获取的once,才能正确用await控制时序:
async function CheckForPioneer(){ // 一次性读取 pioneer 邮箱列表 const emailSnapshot = await pioneer_emails.once('value'); gotEmail(emailSnapshot); // 邮箱不在列表中再查成就 if(!pioneer) { const achievementSnapshot = await player_achievements.once('value'); if(achievementSnapshot.exists()) { gotAchievement(achievementSnapshot); } else { noPioneer(); } } }
3. 渲染逻辑加全量空值判断
现有pioneer条件渲染已经可以避免非先锋用户展示定居点字段,再加可选链完全规避报错:
{/* 玩家名称渲染 */} <h2>先祖 {playerName}</h2> {/* 定居点渲染 */} { pioneer && ( <div> <center><h3>建立者所属:</h3></center> <center><h2>{settlementName} 定居点</h2></center> </div> )}
关于空值填充的疑问
不需要单独在NoPioneer里填充空值,上述默认结构合并+可选链的方案已经可以完全覆盖空值场景,性能和可维护性更好。如果需要兼容老数据,可以在拉取到用户数据后判断是否有settlement字段,缺失的话主动补入默认值写入Firebase即可。
内容的提问来源于stack exchange,提问作者BettyWhite
相关产品推荐
相关产品推荐

