NextJS中user.displayName读取报错:Cannot read properties of null
解决NextJS+Firebase中
user.displayName读取null属性的错误 问题场景
我在NextJS结合Firebase开发时,已在_app.js中通过useState监听auth.onAuthStateChanged状态,将user props传递给所有组件。在[postid].jsx组件中,执行console.log(user)能输出包含displayName属性的用户对象,但执行console.log(user.displayName)时却抛出错误:TypeError: Cannot read properties of null (reading 'displayName')。该问题仅出现在[postid].jsx文件中,且console.log(user)会同时输出null和用户对象。
相关代码
[postid].jsx
const post = ({ post, user }) => { const [name, setName] = useState(""); const [addComment, setAddComment] = useState(""); const router = useRouter(); const { postid } = router.query; console.log(user); // 会输出null和用户对象 console.log(user.displayName); // 抛出错误 const handleSubmit = (e) => { e.preventDefault(); if (!addComment || !name) { alert("Please fill all the fields proplerly!"); } else { db.collection("posts").doc(postid).collection("comments").add({ name: name, comment: addComment, }); } }; return ( <div className="post container text-center"> <h2>{post.title}</h2> <h5>Created On - {new Date(post.createdAt).toDateString()}</h5> <br /> <img src={post.imageUrl} alt={post.alt} /> <br /> <p>{post.description}</p> <br /> <form> <div className="form-input"> <input type="text" name="name" id="name" className="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter Your Name" /> </div> <div className="form-input"> <textarea name="add-comment" id="add-comment" className="add-comment" value={addComment} onChange={(e) => setAddComment(e.target.value)} cols="100" rows="3" placeholder="Add your Comment" ></textarea> </div> <button onClick={handleSubmit} className="btn-warning"> Submit </button> </form> <div className="comments"> <p className="comment"> <b> <i>Name</i>: </b>{" "} <span>Comment</span> </p> </div> </div> ); }; export default post; export async function getServerSideProps({ params }) { const result = await db.collection("posts").doc(params.postid).get(); return { props: { post: { ...result.data(), createdAt: result.data().createdAt.toMillis(), }, }, }; }
_app.js
export default function App({ Component, pageProps }) { const [user, setUser] = useState(null); useEffect(() => { auth.onAuthStateChanged((user) => { if (user) { setUser(user); } else { setUser(null); } }); }, []); return ( <div className="app"> <div className="banner-div"> <Navbar user={user} /> <img src="/banner.jpg" alt="School Banner" className="banner" /> </div> <Component {...pageProps} user={user} /> </div> ); }
问题原因
核心是异步渲染的时序问题:
_app.js中user的初始状态是null,所有子组件会先以这个初始值完成第一次渲染。auth.onAuthStateChanged是异步方法,Firebase需要时间完成用户身份验证状态的拉取,回调函数触发后才会更新user为真实用户对象。- 所以
[postid].jsx会执行两次渲染:第一次user为null,第二次才是完整的用户对象。直接访问user.displayName时,第一次渲染就会因为user是null而抛出错误,而console.log(user)会打印两次结果。
解决方案
方案1:使用可选链操作符(推荐)
用?.语法在访问属性前自动判断user是否为null/undefined,避免报错:
console.log(user?.displayName); // 也可以配合默认值使用 const userName = user?.displayName || "匿名用户";
方案2:添加条件判断
在访问user属性前先做非空检查,或者在组件渲染时添加加载状态:
// 仅在user存在时打印属性 if (user) { console.log(user.displayName); } // 组件渲染时先显示加载状态 if (!user) { return <div>加载中...</div>; } return ( // 原组件内容 );
方案3:优化_app.js初始状态
先尝试获取auth.currentUser(如果用户之前登录过,这个值可能在初始化时就存在),减少null状态的渲染次数:
export default function App({ Component, pageProps }) { const [user, setUser] = useState(null); useEffect(() => { // 先检查当前已登录的用户 const currentUser = auth.currentUser; if (currentUser) { setUser(currentUser); } // 监听状态变化并在组件卸载时取消监听 const unsubscribe = auth.onAuthStateChanged((user) => { setUser(user || null); }); return () => unsubscribe(); }, []); return ( <div className="app"> <div className="banner-div"> <Navbar user={user} /> <img src="/banner.jpg" alt="School Banner" className="banner" /> </div> <Component {...pageProps} user={user} /> </div> ); }
内容的提问来源于stack exchange,提问作者Agrim Singh
相关产品推荐
相关产品推荐

