You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:24:25