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

React输入框在Inertia.js useForm状态更新后失去焦点的问题求助

React输入框在Inertia.js useForm状态更新后失去焦点的问题求助

大家好,我最近在开发自定义表单组件时遇到了一个棘手的问题:用Inertia.js的useForm hook管理表单状态,每次在输入框敲入字符触发状态更新后,输入框就会失去焦点。我已经排查过输入框的key,确认它没有频繁变化,而且观察到每次输入都会触发表单甚至页面的重渲染,这应该就是失焦的根源,但不知道该怎么修复。

先贴一下我的代码:

Form组件代码

type FormField<T> ={
  name: keyof T;
  label: string;
  type: "text" | "textarea";
  group?: string;
}

interface Props<T extends Record<string, any>> {
  initialData: T
  fields: FormField<T>[]
}

export function Form<T extends Record<string, any>>(props: Props<T>) {
  const { initialData, fields, formLayoutKey } = props;
  const { data, setData, errors, patch } = useForm<T>((initialData ?? {}) as T);

  const onSubmit = (e?: React.FormEvent) => {
    e?.preventDefault();
    console.log(data)
  }

  const renderFieldsByGroup = (group: string) => {
    return fields
      .filter((f) => f.group === group)
      .map((field, index) => {
        // 之前这里硬编码了name,不过不影响失焦问题
        const value = data.name
        const error = errors[field.name as string];
        const key = `${index}-${field.name}`
        switch (field.type) {
          case 'text':
            return (
              <FieldSet key={key} label={field.label} error={error}>
                {/* 同样硬编码了"name",但不影响失焦 */}
                <Input type="text" placeholder={field.label} data={value ?? ''} onChange={e => setData("name", e.target.value)} />
              </FieldSet>
            );
          default:
            return null;
        }
      });
  };

  return (
    <form onSubmit={onSubmit}>
      <FormLayout>
        <div key="general">
          {renderFieldsByGroup("myGroup")}
        </div>
      </FormLayout>
    </form>
  );
}

Input组件代码

interface Props {
  type?: string
  placeholder?: string
  classes?: string
  onChange?: (e: any) => void
  data?: string
}

export default function Input(props: Props) {
  const { type = "text", placeholder, classes, onChange, data} = props
  return (
    <input 
      type={type ?? "text"} 
      placeholder={placeholder} 
      className={`${type === "checkbox" ? "checkbox" : "input border"} ${classes}`} 
      onChange={onChange}
      value={typeof data === "string" || typeof data === "number" ? data : ""}
      checked={type === "checkbox" ? Boolean(data) : undefined}
    />
  );
}

问题分析&解决方案

经过排查,这个问题的核心原因是每次状态更新时,渲染字段的函数被重新创建,导致子组件(FieldSet、Input)被频繁卸载再挂载,自然就失去了焦点。下面是具体的修复步骤:

1. 用useCallback缓存渲染函数

renderFieldsByGroup在Form组件的每次重渲染中都会被重新定义,这会让它返回的子组件触发重挂载。我们可以用useCallback缓存这个函数,只在依赖项变化时才重新创建:

import { useCallback } from 'react';

// 修改Form组件中的renderFieldsByGroup
const renderFieldsByGroup = useCallback((group: string) => {
  return fields
    .filter((f) => f.group === group)
    .map((field, index) => {
      // 这里要修正硬编码的name,改成动态绑定field.name
      const value = data[field.name];
      const error = errors[field.name as string];
      // 把name放在key前面,可读性更好,也更稳定
      const key = `${field.name}-${index}`;
      switch (field.type) {
        case 'text':
          return (
            <FieldSet key={key} label={field.label} error={error}>
              <Input 
                type="text" 
                placeholder={field.label} 
                data={value ?? ''} 
                // 同样修正硬编码的字段名,绑定到当前field的name
                onChange={e => setData(field.name, e.target.value)} 
              />
            </FieldSet>
          );
        default:
          return null;
      }
    });
}, [fields, data, errors, setData]); // 传入必要的依赖项

注意:我顺便修正了你代码里硬编码字段名的问题——之前所有输入框都绑定到name字段,这不仅逻辑错误,也可能加重重渲染的问题,现在改成动态绑定每个field的name。

2. 优化Input组件的稳定性

虽然你的Input是单独导出的,已经比较稳定,但可以优化一下props的处理逻辑,避免不必要的渲染:

export default function Input(props: Props) {
  const { type = "text", placeholder, classes, onChange, data} = props;
  
  // 统一处理输入框的value/checked属性
  const inputProps = type === "checkbox" 
    ? { checked: Boolean(data), value: data }
    : { value: typeof data === "string" || typeof data === "number" ? data : "" };

  return (
    <input 
      type={type} 
      placeholder={placeholder} 
      className={`${type === "checkbox" ? "checkbox" : "input border"} ${classes}`} 
      onChange={onChange}
      {...inputProps}
    />
  );
}

3. 可选:用React.memo优化Form组件

如果Form的父组件会频繁重渲染,也会导致Form跟着重渲染。可以用React.memo包裹Form组件,避免不必要的重渲染:

import { memo } from 'react';

export const Form = memo(function Form<T extends Record<string, any>>(props: Props<T>) {
  // 组件内部代码不变
}) as typeof Form;

注意:如果父组件传递的initialData或fields是每次渲染都重新创建的对象/数组,memo会失效,这时候需要在父组件用useMemo缓存这些props。


经过这些修复,输入框应该就不会在状态更新时失去焦点了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 07:38:05