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

React中动态添加的表单输入框输入字符时丢失焦点问题排查

问题:React动态生成的输入框输入时丢失焦点

我在实现基于数组长度动态添加表单字段的功能时,遇到了输入框焦点丢失的问题——每次在任意输入框中输入字符,焦点都会立刻消失。以下是我的代码实现:

const [otherSources, setOtherSources] = useState<Source[]>([])
const SourceView = (source: Source & { index: number }) => {
    return <div>
        <div className="field">
            <label className="label">Name</label>
            <div className="control">
                <input className="input" onChange={(event) => {
                    const copyOtherSources = otherSources
                    const sourceElement = copyOtherSources[source.index]
                    sourceElement.name = event.target.value
                    setOtherSources([...copyOtherSources])
                }} value={source.name} />
            </div>
        </div>
        <div className="field">
            <label className="label">Url</label>
            <div className="control">
                <textarea className="textarea" onChange={(event) => {
                    const copyOtherSources = otherSources
                    const sourceElement = copyOtherSources[source.index]
                    sourceElement.url = event.target.value
                    setOtherSources([...copyOtherSources])
                }} rows={2} value={source.url} />
            </div>
        </div>
    </div>
}
return (
    <div className="field">
        <label className="label">Other Sources (comma separated)</label>
        {
            (otherSources?.length ?? 0) > 0 &&
            otherSources.map((source, index) => {
                return <SourceView {...source} index={index} key={`field${index}`} />
            })
        }
    </div>
)

问题原因

  1. 组件定义位置错误:SourceView被定义在父组件的渲染逻辑内部,每次父组件因状态更新重渲染时,SourceView都会被重新定义为一个全新的组件类型。
  2. 组件渲染方式问题:使用<SourceView />组件标签语法调用时,React会将其识别为独立组件。每次父组件重渲染,新的SourceView组件类型会导致旧组件被卸载、新组件重新挂载,输入框自然会丢失焦点。

修复方案

  1. 将SourceView移到父组件外部定义:避免每次父组件重渲染都重新创建组件类型,保证组件的一致性。
  2. 改用函数调用替代组件标签:直接调用SourceView()函数生成JSX,这样它会被当作普通的渲染片段,而非独立组件,不会触发卸载重挂载。
  3. 优化状态更新逻辑:原代码中直接修改状态数组的元素属于可变操作,改为通过不可变方式更新状态,避免潜在的状态不一致问题。

修复后的代码示例:

// 将SourceView移到父组件外部,确保组件类型稳定
const SourceView = (props: {
    source: Source;
    index: number;
    updateSource: (index: number, key: 'name' | 'url', value: string) => void;
}) => {
    const { source, index, updateSource } = props;
    return (
        <div>
            <div className="field">
                <label className="label">Name</label>
                <div className="control">
                    <input
                        className="input"
                        onChange={(event) => updateSource(index, 'name', event.target.value)}
                        value={source.name}
                    />
                </div>
            </div>
            <div className="field">
                <label className="label">Url</label>
                <div className="control">
                    <textarea
                        className="textarea"
                        onChange={(event) => updateSource(index, 'url', event.target.value)}
                        rows={2}
                        value={source.url}
                    />
                </div>
            </div>
        </div>
    );
};

// 父组件
const ParentComponent = () => {
    const [otherSources, setOtherSources] = useState<Source[]>([]);

    // 抽离不可变更新逻辑
    const updateSource = (index: number, key: 'name' | 'url', value: string) => {
        setOtherSources((prevSources) => {
            const newSources = [...prevSources];
            // 创建新对象,避免修改原状态元素
            newSources[index] = { ...newSources[index], [key]: value };
            return newSources;
        });
    };

    return (
        <div className="field">
            <label className="label">Other Sources (comma separated)</label>
            {otherSources.length > 0 &&
                otherSources.map((source, index) => {
                    // 直接调用函数生成JSX
                    return SourceView({ source, index, updateSource });
                })}
        </div>
    );
};

内容的提问来源于stack exchange,提问作者Tarun Chawla

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:19:54