React内部函数组件表单input输入框无法输入问题修复方案
问题根本原因
问题出在你将RegisterFormInputs、RegisterVerificationForm两个函数组件定义在了父组件RegisterForm的函数作用域内部:
- 每次父组件因为状态更新触发重渲染时,都会重新生成这两个子组件函数的全新引用,和上一次渲染时的组件引用完全不一致。
- React对比虚拟DOM时,只要发现组件类型的引用变了,就会判定这是完全不同的新组件,直接卸载上一次渲染的旧组件实例、挂载全新的组件实例,不会做状态复用。
- 你在输入框输入内容时,
onChange触发父组件状态更新→父组件重渲染→生成全新的RegisterFormInputs组件→旧输入框被卸载、新输入框挂载,输入焦点和已输入内容全部丢失,最终表现为输入框完全无法输入内容。
修复方法
优先选择第一种方案,稳定性和可维护性最好:
- 把两个子组件移到
RegisterForm外部定义为独立组件,子组件需要用到的状态、回调函数通过props传入即可。这样组件引用在所有渲染周期中都是稳定的,React可以正常复用组件实例,输入行为就会恢复正常。
如果你不想把组件抽离到外部,也可以选择第二种方案:
- 不要把这两段UI包装成组件用
<Component />的形式渲染,直接把JSX内联写在返回的模板中;如果要复用JSX逻辑,可以提取成普通函数,渲染时直接执行函数拿到JSX返回值即可,不要把它当组件调用。
修复后核心代码示例
// 把子组件移到父组件外部定义,通过props接收需要的参数 const RegisterFormInputs = ({ handleRegisterOnChange, register }) => { return <div> <h2 className="loginElement">Create profile</h2> <label className="loginElement"> <span className="loginElement">Email</span> <input type="email" name="email" className="loginElement" onChange={handleRegisterOnChange} /> </label> <label className="loginElement"> <span className="loginElement">Password</span> <input type="password" name="password" className="loginElement" onChange={handleRegisterOnChange} /> </label> <button type="button" className="submit" onClick={register}>Register</button> </div> } const RegisterVerificationForm = ({ handleTokenOnChange, confirmRegistration }) => { return <div className="verify-registration"> <h2 className="loginElement text-center">Activate your profile</h2> <div className="d-flex justify-content-center align-items-center"> <TextField id="outlined-basic" label="Verification token" variant="outlined" onChange={handleTokenOnChange} /> <Timer initialMinutes={10}/> </div> <div className="mt-3 text-center"> <Button variant="contained" onClick={confirmRegistration}>Submit</Button> </div> </div>; } function RegisterForm() { const [registerInputValues, setRegisterInputValues] = useState({ email: '', password: '' }); // 原代码缺失验证码相关状态和回调,这里补充基础实现 const [confirmationToken, setConfirmationToken] = useState(''); const handleTokenOnChange = (e) => setConfirmationToken(e.target.value); const [showRegisterForm, setShowRegisterForm] = useState(true); const [showRegisterVerificationForm, setShowRegisterVerificationForm] = useState(false); const handleRegisterOnChange = event => { const { name, value } = event.target; setRegisterInputValues({ ...registerInputValues, [name]: value }); }; const register = () => { studentsService.registerStudent(registerInputValues) .then(response => { console.log(response); }) .catch(e => { console.log(e); }); setShowRegisterForm(false); setShowRegisterVerificationForm(true); } const confirmRegistration = () => { studentsService.confirmRegistration(confirmationToken) .then(response => {console.log(confirmationToken, response) }) .catch(e => {console.log(e); alert(e.response.data);}); } return ( <> <div className="cont mt-5"> <div className="sub-cont"> <div className="form sign-up"> {showRegisterForm ? <RegisterFormInputs handleRegisterOnChange={handleRegisterOnChange} register={register} /> : null} {showRegisterVerificationForm ? <RegisterVerificationForm handleTokenOnChange={handleTokenOnChange} confirmRegistration={confirmRegistration} /> : null} </div> </div> </div> </> ) } export default RegisterForm;
注:原代码中缺失验证码输入框对应的
confirmationToken状态和handleTokenOnChange回调定义,上述示例中已经补充了基础实现,可根据业务需求调整。
内容的提问来源于stack exchange,提问作者Enchantres
相关产品推荐
相关产品推荐

