React表单输入框键入字符后自动失焦问题解决方案
问题根因
输入单字符就自动失焦的核心原因是你将TextInput组件定义在了PersonInnerFormComponent的函数体内部。
每次输入触发状态更新、PersonInnerFormComponent重渲染时,内部定义的TextInput会被重新创建为一个全新的组件引用,React会将其识别为和旧组件完全不同的类型,直接卸载旧的输入框DOM、挂载新的输入框DOM,新挂载的元素自然不会保留之前的输入焦点,这个问题和你使用自定义Hook还是useState存储表单状态没有关联。
解决方案
改动最小的修复方式是把TextInput组件移到PersonInnerFormComponent外层定义,将组件依赖的formData、onChange、error等值通过props传入,从根源避免每次重渲染重新生成组件引用。
修复后的核心代码如下:
import type { FC } from 'react'; // 子组件挪到外层定义,不要嵌套在其他组件的渲染逻辑里 const TextInput: FC<{ id: string; name: string; value: string; onChange: any; error?: unknown; }> = ({ id, name, value, onChange, error }) => { return ( <FormGroup> <Col sm="2"> <FormLabel column htmlFor={id}> {name} </FormLabel> </Col> <Col sm="10"> <FormControl key={id} type="text" id={id} placeholder={name} value={value} onChange={onChange} error={error} /> </Col> </FormGroup> ); }; export const PersonInnerFormComponent: FC<{ formData: IPerson; onChange: any; error?: PersonError; }> = ({ formData, onChange, error }) => { return ( <> <TextInput id="type" name="Type" value={formData.type} onChange={onChange} error={error?.payload.type} /> <TextInput id="name" name="Name" value={formData.name} onChange={onChange} error={error?.payload.name} /> </> ); }; // 外层PersonFormComponent逻辑无需修改 const PersonFormComponent: FC<IPersonFormComponentProps> = ({ error, ...props }) => { const { formData, onChange, onSubmit } = useFormPerson( props.onSubmit, props.person ); return ( <PersonFormWrapper> <Form onSubmit={onSubmit}> <PersonInnerFormComponent formData={formData} onChange={onChange} error={error} /> {props.onCancel && ( <Button variant="outline-dark" className="mr-3 text-capitalize btn-default" onClick={props.onCancel} > Cancelar </Button> )}{' '} <Button variant="success" type="submit"> Guardar </Button>{' '} </Form> </PersonFormWrapper> ); };
注意:React开发规范要求禁止在组件渲染函数内部定义新的子组件,这类写法除了触发输入框失焦,还会导致子组件状态丢失、重复重渲染、性能异常等一系列问题。如果确实需要在组件内部写可复用的渲染逻辑,可以直接抽成渲染函数返回JSX,不要定义为新的组件。
内容的提问来源于stack exchange,提问作者Alvaro Losada
相关产品推荐
相关产品推荐

