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
相关产品推荐
相关产品推荐

