Firebase实时数据库角色路由跳转报错:Cannot read properties of null
问题分析与解决方案
错误原因
firebaseData初始值为null:FirebaseData组件通过异步请求获取数据库数据,初始状态data是null,当auth.onAuthStateChanged触发时,数据可能还未加载完成,此时访问firebaseData.users就会抛出"Cannot read properties of null"错误。- useEffect依赖缺失:监听auth状态的useEffect依赖数组为空,不会响应
firebaseData的更新,即使后续数据加载完成,也不会重新获取用户角色。 - 拼写错误:代码中
setIsAuthenticated(trEue);是笔误,应为setIsAuthenticated(true);。 - 字段名不匹配:数据库中存储角色的字段是
roles,但代码中尝试读取firebaseData.users[user.uid].role,字段名不一致会导致角色获取失败。
修复步骤
1. 修复拼写错误
将setIsAuthenticated(trEue);改为:
setIsAuthenticated(true);
2. 处理异步数据加载问题
修改App.js中的useEffect,添加firebaseData到依赖数组,并在访问users前先判断firebaseData是否存在:
useEffect(() => { const unsubscribe = auth.onAuthStateChanged((user) => { if (user) { setIsAuthenticated(true); setDisplayName(user.displayName); // 先判断firebaseData是否已加载,使用可选链避免空值错误 if (firebaseData?.users) { // 匹配数据库字段名roles setRole(firebaseData.users[user.uid].roles); } } else { setIsAuthenticated(false); setDisplayName(""); setRole(""); } }); // 清理auth状态监听,防止内存泄漏 return unsubscribe; }, [firebaseData]); // 添加firebaseData到依赖数组,数据更新时重新执行
3. 优化FirebaseData组件(可选)
在FirebaseData中添加加载状态,方便App组件判断数据是否就绪:
import {database} from "./firebase-config"; import React, {useState, useEffect} from "react"; import {ref, onValue} from "firebase/database"; const db = database; export default function FirebaseData() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const unsubscribe = onValue(ref(db), (snapshot) => { setData(snapshot.val()); setLoading(false); }); // 清理数据库监听 return unsubscribe; }, []); return { data, loading }; }
在App.js中使用该组件:
const { data: firebaseData, loading } = FirebaseData(); // 数据加载中显示占位内容 if (loading) { return <div>加载中...</div>; }
4. 简化路由逻辑
原路由存在重复定义和逻辑混乱问题,优化后:
return ( <Routes> <Route path="/signup" element={<Signup />} /> <Route path="/signin" element={<SignIn />} /> {/* 未登录状态路由 */} {!isAuthenticated ? ( <Route path="/" element={<Home isAuthenticated={false} />} /> ) : ( // 已登录状态,根据角色分配路由 <> <Route path="/" element={<Home isAuthenticated={true} displayName={displayName} role={role}/>} /> {role === "admin" && ( <> <Route path="/home" element={<Landing />} /> <Route path="/details" element={<PhoneDetails />} /> </> )} </> )} {/* 404页面跳转首页 */} <Route path="*" element={<Navigate replace to="/" />} /> </Routes> );
关键注意事项
- 异步数据处理:Firebase数据获取是异步操作,必须确保数据加载完成后再访问其属性,可使用可选链操作符
?.避免空值错误。 - 订阅清理:
auth.onAuthStateChanged和onValue都返回取消订阅的函数,要在useEffect的清理函数中调用,防止内存泄漏。 - 字段名一致性:确保代码中读取的字段名与Firebase数据库中的字段完全匹配(此处为
roles而非role)。
内容的提问来源于stack exchange,提问作者002 DMAM Jigyasa Nagpal
相关产品推荐
相关产品推荐

