React+Firebase注册页onChange致输入框频繁重置问题排查
React注册页面输入框频繁重置问题修复
问题根源
你把RootStyle、CssTextField这类通过Material-UI styled API创建的自定义组件,定义在了SignUp组件内部。每次输入内容触发状态更新时,SignUp组件会重新渲染,内部的styled组件会被重新创建。React会判定这些是全新的组件类型,进而卸载原有组件并重新挂载,直接导致输入框的状态被重置。
修复方案
把所有styled组件的定义移到SignUp组件外部,确保它们只被初始化一次,不会随组件渲染重复创建。同时补上LastName输入框的状态绑定,完善表单功能。
修复后的完整代码
import React,{useState,useContext} from 'react'; import Button from '@mui/material/Button'; import TextField from '@mui/material/TextField'; import Link from '@mui/material/Link'; import Grid from '@mui/material/Grid'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; import { styled } from '@mui/material'; import Logo from '../assets/QpiVolta-Logo.png' import { useTheme,IconButton } from '@mui/material'; import { tokens } from '../theme'; import {Person,Lock, Mail, Visibility, VisibilityOff} from '@mui/icons-material'; import { useNavigate } from 'react-router-dom'; import { UserAuth } from '../context/AuthContext'; // 将styled组件移到组件外部 const RootStyle = styled("div")(({ theme }) => { const colors = tokens(theme.palette.mode); return { backgroundColor:"#d4cbf6", background:`${colors.primary[400]} !important`, height: "100vh", display: "flex", placeItems: "center", } }); const CssTextField = styled(TextField)(({ theme }) => { const colors = tokens(theme.palette.mode); return { '& label.Mui-focused': { color: `${colors.pinkAccent[500]}`, }, '& .MuiInput-underline:after': { borderBottomColor: `${colors.pinkAccent[500]}`, }, '& .MuiOutlinedInput-root': { '& fieldset': { borderColor: 'grey', }, '&:hover fieldset': { borderColor: `${colors.pinkAccent[500]}`, }, '&.Mui-focused fieldset': { borderColor: `${colors.pinkAccent[500]}`, }, }, } }); const SignUp = () => { const theme = useTheme(); const colors = tokens(theme.palette.mode); const [showPassword, setShowPassword] = useState(false); const handleClickShowPassword = () => setShowPassword((show) => !show); const handleMouseDownPassword = (event) => { event.preventDefault(); }; const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [firstName,setFirstName] = useState("") const [lastName,setLastName] = useState("") // 新增LastName状态 const [error, setError] = useState("") const {signUp} = UserAuth() const navigate = useNavigate() const handleSubmit= async(e) => { e.preventDefault() try { await signUp(email,password) setError('') navigate('/') } catch (error) { console.log(error) setError(error.message) } } const logoStyle = { height: '80px', }; return ( <RootStyle theme={theme}> <Container maxWidth="sm" > <Box sx={{ marginTop: 8, display: 'flex', flexDirection: 'column', alignItems: 'center', }} > <img component="img" src={Logo} style={logoStyle} /> <Typography component="h1" variant="h2" color={colors.grey[100]} > Create an account<span style={{ color:`${colors.pinkAccent[500]}`}}>.</span> </Typography> <Box component="form" onSubmit={handleSubmit} sx={{ mt: 2 , mb:2}}> <Grid container spacing={3} > <Grid item xs={12} sm={6} > <Box sx={{ display: 'flex', alignItems: 'flex-end' }}> <Person sx={{ color: 'action.active', mr: 1, my: 0.5 }} /> <CssTextField fullWidth label="First Name" id="FirstName" name="FirstName" autoComplete="family-name" variant='standard' value={firstName} onChange={(e)=> setFirstName(e.target.value)} /> </Box> </Grid> <Grid item xs={12} sm={6}> <Box sx={{ display: 'flex', alignItems: 'flex-end' }}> <Person sx={{ color: 'action.active', mr: 1, my: 0.5 }} /> <CssTextField fullWidth id="lastName" label="Last Name" name="lastName" autoComplete="family-name" variant='standard' value={lastName} onChange={(e)=> setLastName(e.target.value)} // 绑定LastName状态 /> </Box> </Grid> <Grid item xs={12}> <Box sx={{ display: 'flex', alignItems: 'flex-end' }}> <Mail sx={{ color: 'action.active', mr: 1, my: 0.5 }} /> <CssTextField required fullWidth id="email" label="Email Address" name="email" value={email} autoComplete="email" variant='standard' onChange={(e) => { console.log(e.target.value); setEmail(e.target.value); }} /> </Box> </Grid> <Grid item xs={12}> <Box sx={{ display: 'flex', alignItems: 'flex-end' }}> <Lock sx={{ color: 'action.active', mr: 1, my: 0.5 }} /> <CssTextField required fullWidth name="password" label="Password" id="password" autoComplete="new-password" type={showPassword ? 'text' : 'password'} variant='standard' value={password} onChange={(e) => setPassword(e.target.value)} InputProps={{ endAdornment: ( <IconButton aria-label="toggle password visibility" onClick={handleClickShowPassword} onMouseDown={handleMouseDownPassword} > {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> ), }} /> </Box> </Grid> </Grid> <Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }} > Sign Up </Button> <Grid container justifyContent="flex-end"> <Grid item> <Link href="/Login" variant="body2" sx={{color:"grey"}}> Already have an account? Sign in </Link> </Grid> </Grid> </Box> </Box> </Container> </RootStyle> ) } export default SignUp
额外说明
- 调整了styled组件的定义方式,通过接收
theme参数来获取colors,避免在组件内部重复计算。 - 把原来的
name状态改为firstName,新增lastName状态并绑定到对应的输入框,完善表单数据收集。
内容的提问来源于stack exchange,提问作者Rio
相关产品推荐
相关产品推荐

