Next.js使用自定义React Hook报Objects are not valid as a React child错误
问题排查与解决方案
错误原因
报错和自定义Hook的setUsername逻辑无关,核心触发点是Enter页面的渲染分支直接调用了router.push()方法:router.push()是Next.js提供的异步路由跳转方法,调用后会返回Promise对象,你直接将它写在JSX的条件渲染分支中,React尝试将这个Promise作为子元素渲染,就触发了「Objects are not valid as a React child (found: [object Promise])」的错误。
而setUsername执行时会触发消费UserContext的组件重渲染,刚好走到了router.push的分支,所以会被误定位为报错来源。
修复方案
1. 核心修复逻辑
不要在JSX的渲染分支中直接执行路由跳转,用useEffect监听user和username的状态变化,满足跳转条件时再触发跳转。
2. 额外优化点
- 异步事件处理函数中返回的JSX不会被渲染,需要用state存储错误信息再渲染到视图中
- React中DOM的
class属性要写成className,for属性要写成htmlFor避免控制台警告
修改后的完整代码
import { useRouter } from "next/dist/client/router"; import { useContext, useState, useEffect } from "react"; import { UserContext } from "../lib/Context"; import { auth, googleAuthProvider, firestore} from "../lib/firebase"; export default function Enter({}) { const { user, username } = useContext(UserContext); const router = useRouter(); // 用useEffect处理跳转逻辑,不要直接写在JSX里 useEffect(() => { if (user && username) { router.push("/"); } }, [user, username, router]); return ( <> <div className="container my-3"> {/* 只渲染需要展示的组件,跳转逻辑放到effect里 */} {!user ? <Login /> : !username ? <UserNameForm /> : null} </div> </> ); } // 登录组件修改错误提示逻辑 const Login = () => { const [errorMsg, setErrorMsg] = useState(''); const signinwithgoogle = async () => { try { setErrorMsg(''); await auth.signInWithPopup(googleAuthProvider); } catch (error) { console.error(error.message); setErrorMsg('登录过程出现错误,请重试'); } }; return ( <> <div className="d-flex justify-content-center"> <h3>使用谷歌账号注册或登录</h3> </div> {errorMsg && <div className="d-flex justify-content-center text-danger my-2">{errorMsg}</div>} <div className="d-flex justify-content-center my-4"> <button onClick={signinwithgoogle} className="btn btn-primary"> 注册 <i className="fab fa-google"></i> </button> </div> </> ); }; // 用户名提交表单 const UserNameForm = () => { const [formValue, setFormValue] = useState(""); const {user} = useContext(UserContext) const onChange = (e) => { setFormValue(e.target.value); }; const onSubmit = async (e) => { e.preventDefault(); // 批量写入用户数据 const userDoc = firestore.doc(`users/${user.uid}`); const usernameDoc = firestore.doc(`usernames/${formValue}`); const batch = firestore.batch(); batch.set(userDoc, { username: formValue, photoURL: user.photoURL, displayName: user.displayName }); batch.set(usernameDoc, { uid: user.uid }); await batch.commit(); }; return ( <> <form onSubmit={onSubmit}> <div className="mb-3"> <label htmlFor="usernameInput" className="form-label"> 请输入用户名 </label> <input value={formValue} onChange={onChange} type="text" className="form-control" id="usernameInput" minLength={4} required /> </div> <button type="submit" className="btn btn-primary"> 提交 </button> </form> </> ); };
内容的提问来源于stack exchange,提问作者arsh coding
相关产品推荐
相关产品推荐

