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

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,提问作者Никита Лямный

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:51:16