React input onChange触发重渲染导致输入框失焦问题修复
问题根因
输入框失焦和你用统一对象维护表单状态没有任何关系,核心问题是你将FormControl组件定义在了ControlledInputs父组件的函数体内部。
每次父组件因状态更新触发重渲染时,都会生成一个全新的FormControl函数引用,React在虚拟DOM对比阶段会判定这是完全不同的组件类型,会直接销毁旧的输入框DOM、挂载新的输入框DOM,直接导致焦点丢失。
修复方案
完全不需要拆分出多个独立useState,保留统一person状态对象的前提下,按以下方式调整即可:
- 将嵌套在父组件内的
FormControl移动到组件外层定义,保证组件引用在重渲染过程中保持稳定 - 将子组件依赖的表单值、变更回调通过props传入,不要让子组件直接闭包引用父组件内的变量
- 顺手修正原有代码的小问题:表单提交事件绑定到form标签上、年龄输入框type修正为
number、状态更新使用函数式写法避免闭包拿到旧值、列表渲染变量重名问题调整
修复后的完整可运行代码:
import React, { useState } from 'react'; function FormControl({ idname, type, text, value, onChange }){ return( <div className='form-control'> <label htmlFor={idname}>{text} : </label> <input type={type} id={idname} name={idname} value={value} onChange={onChange} /> </div> ) } const ControlledInputs = () => { const [person, setPerson] = useState({ firstName: '', email: '', age: '' }); const [people, setPeople] = useState([]); const handleChange = (e) => { const name = e.target.name; const value = e.target.value; setPerson(prev => ({ ...prev, [name]: value })); }; const handleSubmit = (e) => { e.preventDefault(); if (person.firstName && person.email && person.age) { const newPerson = { ...person, id: Date.now().toString() }; setPeople(prev => [...prev, newPerson]); setPerson({ firstName: '', email: '', age: '' }); } }; return ( <> <article className='form'> <form onSubmit={handleSubmit}> <FormControl idname="firstName" type="text" text="First name" value={person.firstName} onChange={handleChange} /> <FormControl type="email" idname="email" text="Email" value={person.email} onChange={handleChange} /> <FormControl type="number" idname="age" text="Age" value={person.age} onChange={handleChange} /> <button type='submit' className='btn'> add person </button> </form> </article> <article> {people.map((item) => { const { id, firstName, email, age } = item; return ( <div key={id} className='item'> <h4>{firstName}</h4> <p>{email}</p> <p>{age}</p> </div> ); })} </article> </> ); }; export default ControlledInputs;
调整后输入时不会再出现焦点丢失问题,同时完全保留了统一维护表单状态的实现逻辑。
内容的提问来源于stack exchange,提问作者Никита Лямный
相关产品推荐
相关产品推荐

