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

React拆分组件子部分时状态更新致输入框失焦的解决方法

问题根源

你在FormBasic内部定义的Form、Actions属于未做缓存的临时函数组件,每次FormBasic状态更新触发重渲染时,都会生成全新的函数引用。React的Diff算法判定为全新组件,会直接卸载旧组件实例、挂载新实例,因此输入框会出现失焦问题。

解决方案

方案1:直接传递JSX元素(最轻量化,推荐)

直接把Form、Actions从函数组件改为JSX元素,不需要额外引入任何API,修改成本最低:

// 修改后的FormBasic组件
import  { useState } from 'react'

const FormBasic = ({ children }) => {
  const [ text, setText ] = useState('')

  const onSubmit = () => console.log("Submitting");
  const onChange = (e) => {
    console.log("Typing", e.target.value);
    setText(e.target.value)
  }

  // 直接返回JSX元素,而非函数组件
  const Form = <textarea rows={4} onChange={onChange} value={text} />;
  const Actions = <button onClick={onSubmit}>Some Buttons</button>;

  return children({ Form, Actions });
};

export default FormBasic;

调用时直接渲染元素即可,不需要加括号执行:

export default function App() {
  return (
      <FormBasic> 
      {({ Form, Actions }) => {
          return (<>
                  <div style={{ border: '1px solid red'}}>
                    {Form}
                  </div>
                  <div style={{ border: '1px solid blue'}}>
                     {Actions}
                  </div>
            </>)
      }}
      </FormBasic>
  );
}

方案2:缓存组件引用(保留函数调用形式)

如果需要给Form/Actions传递自定义参数,必须保留函数调用形式,可以用useCallback缓存函数引用,保证引用稳定性:

import  { useState, useCallback } from 'react'

const FormBasic = ({ children }) => {
  const [ text, setText ] = useState('')

  const onSubmit = () => console.log("Submitting");
  const onChange = (e) => {
    console.log("Typing", e.target.value);
    setText(e.target.value)
  }

  // 用useCallback缓存组件引用,依赖项仅包含用到的外部变量
  const Form = useCallback(() => <textarea rows={4} onChange={onChange} value={text} />, [text]);
  const Actions = useCallback(() => <button onClick={onSubmit}>Some Buttons</button>, []);

  return children({ Form, Actions });
};

export default FormBasic;

调用方式保持你原来的写法即可。

其他轻量化拆分思路

你也可以用复合组件模式,把Form、Actions作为FormBasic的静态属性,内部用React内置的Context共享状态(Context是React原生能力,重量极低,符合轻量化要求),调用写法更直观:

const FormContext = React.createContext();

const FormBasic = ({ children }) => {
  const [ text, setText ] = useState('')
  const onSubmit = () => console.log("Submitting");
  const onChange = (e) => setText(e.target.value);
  
  return (
    <FormContext.Provider value={{text, onChange, onSubmit}}>
      {children}
    </FormContext.Provider>
  )
}

FormBasic.Form = () => {
  const {text, onChange} = React.useContext(FormContext);
  return <textarea rows={4} onChange={onChange} value={text} />
}

FormBasic.Actions = () => {
  const {onSubmit} = React.useContext(FormContext);
  return <button onClick={onSubmit}>Some Buttons</button>
}

// 调用方式
export default function App() {
  return (
    <FormBasic>
      <div style={{ border: '1px solid red'}}>
        <FormBasic.Form />
      </div>
      <div style={{ border: '1px solid blue'}}>
        <FormBasic.Actions />
      </div>
    </FormBasic>
  )
}

你原来的写法本身是可行的,只是没有处理组件引用稳定性的问题,按上述方案调整后即可正常使用。

内容的提问来源于stack exchange,提问作者Someone Special

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 11:45:03