React useEffect问题:自动登录失效、userToken渲染报错
React+Firebase useEffect自动登录问题修复
问题原因
问题1:点击提交不触发登录、手动调用方法崩溃
- Firebase初始化、数据库实例创建逻辑写在组件函数体内,组件每次重渲染都会重复执行
initializeApp,Firebase不允许重复初始化同配置应用,直接抛错崩溃。 useEffect未传依赖数组,每次渲染都会重复执行,产生大量无效数据库请求,和重复初始化逻辑叠加进一步触发报错。- Firebase数据库
set写入是异步操作,点击按钮触发写入后没有等待写入完成就结束流程,此时数据库中还未存入对应用户数据,读取逻辑自然拿不到值。后续输入字符触发组件重渲染、useEffect重新执行读取时,数据已经写入完成,才会出现“输一个字符才完成登录”的现象。 - 错误使用
while循环做条件渲染,React渲染流程中用同步while+return的写法存在死循环风险,属于不规范用法。
问题2:渲染{userToken}抛对象不合法错误
setuserToken存入的是snapshot.val()返回的对象,结构为{username: 输入的用户名},React不支持直接将普通JS对象作为子节点渲染,直接写{userToken}必然触发报错。
修复方案
- 将Firebase初始化逻辑移到组件外部,保证整个应用生命周期只初始化一次,从根源解决重复初始化崩溃问题。
- 给
useEffect传入正确依赖数组,仅在组件首次挂载、钱包地址address变化时执行用户数据读取,避免无效请求。 - 将数据库写入、读取方法改为异步函数,点击提交时等待写入完成后再触发数据拉取,保证能读到最新写入的用户数据,不需要额外输入字符触发更新。
- 替换
while条件渲染为标准的if/else条件返回,符合React渲染规范。 - 渲染用户信息时取对象内的具体属性(如
userToken.username),不要直接渲染整个用户对象。
修复后完整代码
import React, { useState, useEffect } from "react"; import {initializeApp} from 'firebase/app'; import { getDatabase, ref, set, child, get} from 'firebase/database'; // Firebase全局初始化,仅执行一次 const clientCredentials = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, } const app = initializeApp(clientCredentials); const db = getDatabase(); export default function Username(props) { const [userName, setUsername] = useState(''); // 初始值设为null,区分未加载/未登录状态 const [userToken, setuserToken] = useState(null) const address = props.address; const dbRef = ref(db); async function writeUserData(userId, userN) { const reference = ref(db, 'users/' + userId); await set(reference, { username: userN, }); } async function connectUser() { try { const snapshot = await get(child(dbRef, `users/${address}`)) if (snapshot.exists()) { setuserToken(snapshot.val()); } else { console.log("No data available"); setuserToken(null) } } catch (error) { console.error(error); } } const handleChange = (event) => setUsername(event.target.value); async function handleClick(e) { // 等待写入完成再拉取最新用户数据 await writeUserData(address, userName); connectUser(); } // 仅在组件挂载、address变化时执行用户校验 useEffect(() => { connectUser() }, [address]); // 未登录/未加载到用户数据时返回表单 if (!userToken) { return ( <> <div> <p className = "account-Info" >address: {address}</p> </div> <div id="form"> <h2 className='user-Create' > Username </h2> <form id='set-User'> <input id='username' className="user-Create" type='text' value={userName} onChange={handleChange} required minLength='3' maxLength='30' pattern="[a-zA-Z0-9_]+" title='only letters, numbers, and underscores.'/> <button className='user-Create' type="button" onClick={handleClick}>Link Name</button> </form> </div> </> ); } // 已登录状态,取对象具体字段渲染 return ( <p className = "account-Info" >hi {userToken.username}</p> ); }
内容的提问来源于stack exchange,提问作者user19333414
相关产品推荐
相关产品推荐

