You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何从useEffect获取Firebase数据并设置为TextField的defaultValue

问题根本原因

MUI 的 TextField 的 defaultValue 属于非受控组件属性,仅在组件首次挂载渲染时生效,后续数据更新不会触发该属性重新赋值。你发起的 Firebase 异步请求是在组件挂载后才执行的,此时 TextField 已经完成首次渲染,自然拿不到后续返回的 displayname 值。


解决方案1:使用受控组件模式(首选)

直接将 TextField 改为受控组件,把请求回来的默认值同步到你的输入状态上即可,修改后的代码如下:

import React from 'react'
import { useEffect, useState } from 'react';
import { Grid, TextField} from '@mui/material';
import { useAuth } from '../../contexts/AuthContext';
import { db } from '../../firebase'
import { getDoc, doc } from '@firebase/firestore';

// 注意组件名不要用use开头,use开头是React Hook的专属命名规范,普通组件建议改为EditUser
export default function EditUser() {
    const { currentUser } = useAuth();
    const [newDisplayName,setDisplayName] = useState("");
    const documentId = currentUser.uid
    // 初始值改成空对象,避免访问属性时报错
    const [userData, setUserData] = useState({});
    // 加载状态可用来做加载提示、输入禁用
    const [loading, setLoading] = useState(true);

    useEffect(() =>{
        const getUsers = async()=>{
            try {
                const docRef =  doc(db, "user", documentId)
                const res = await getDoc(docRef)
                const fields = res.data().user_fields
                setUserData(fields)
                // 把返回的用户名同步到输入框状态
                setDisplayName(fields.displayname)
                setLoading(false)
            } catch(err) {
                console.log(err)
                setLoading(false)
            }
        }
        getUsers()
    },[]);
    

    return (
        <form>
            <Grid container columnSpacing={1} rowSpacing={1} direction="row" justifyContent="center" alignItems="center" marginBottom={1}>
                <Grid item xs={11} sm={8} md={3}>
                    {/* 用value绑定状态,不要用defaultValue */}
                    <TextField 
                        value={newDisplayName} 
                        error={newDisplayName===""} 
                        helperText={newDisplayName === "" ? 'Required' : ' '} 
                        label="User Display Name*" 
                        id="outlined-required" 
                        type="text" 
                        variant="outlined"  
                        onChange={(event) => {setDisplayName(event.target.value)}} 
                        sx={{width: '100%'}} 
                        disabled={loading} 
                    />
                </Grid>
            </Grid>     
        </form>
    )
}

解决方案2:等待数据加载完成后再渲染组件

如果不想用受控模式,可以等异步请求拿到数据之后再渲染 TextField,保证首次渲染时 defaultValue 已经有值:

// 仅展示修改的渲染部分逻辑
return (
    <form>
        <Grid container columnSpacing={1} rowSpacing={1} direction="row" justifyContent="center" alignItems="center" marginBottom={1}>
            <Grid item xs={11} sm={8} md={3}>
                {/* 只有加载完成后才渲染TextField */}
                {!loading && <TextField 
                    defaultValue={userData.displayname} 
                    error={newDisplayName===""} 
                    helperText={newDisplayName === "" ? 'Required' : ' '} 
                    label="User Display Name*" 
                    id="outlined-required" 
                    type="text" 
                    variant="outlined"  
                    onChange={(event) => {setDisplayName(event.target.value)}} 
                    sx={{width: '100%'}} 
                />}
            </Grid>
        </Grid>     
    </form>
)

原有代码额外优化点

  • 你定义的组件名是 useEditUser,不符合 React 命名规范,use 开头仅用于自定义 Hook,普通组件建议改为 EditUser
  • userData 初始值设为数组 [] 错误,你接口返回的是对象类型,初始值应该设为 {},避免首次访问 userData.displayname 出现异常。

内容的提问来源于stack exchange,提问作者MarcShayne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 17:15:04