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

React中MUI文本输入框输入时立即失焦问题求助

React表单输入框输入单个字符后失焦问题排查与解决

问题现象

在React应用中搭建表单时,输入框每次只能输入一个字符,之后就会自动失焦,必须重新点击才能继续输入。简化代码后发现,使用自定义的PageWrapper(通过styled-components封装的Box)时问题出现,直接替换为原生Box组件则输入恢复正常。

问题代码重现

简化后的问题代码:

const EnterDetails = () => {
  const [formState, setFormState] = React.useState({
    firstName: '',
    lastName: '',
  });

  const onChange = (fieldName: string, newValue: any) => {
    const newState = { ...formState };
    newState[fieldName] = newValue.target.value;
    setFormState({ ...newState });
  };

  // 问题根源:在组件内部定义styled组件
  const PageWrapper = styled(Box)(({ theme }) => ({
    marginTop: theme.spacing(4),
    marginLeft: 'auto',
    marginRight: 'auto',
    textAlign: 'center',
    maxWidth: '800px',
  }));

  return (
        <PageWrapper>
          <BootstrapInput
            required={true}
            value={formState.firstName}
            onChange={(v) => onChange('firstName', v)}
          />
          <BootstrapInput
            required={true}
            value={formState.lastName}
            onChange={(v) => onChange('lastName', v)}
          />
        </PageWrapper>
  );
};

export default EnterDetails;

原因解释

根本原因是在组件内部定义styled组件会导致每次组件渲染时,都重新创建一个全新的PageWrapper组件类型。

React的diff算法通过对比组件类型和key判断是否复用旧组件:

  • 当setFormState触发组件重新渲染时,PageWrapper会被重新定义,此时React会认为当前的PageWrapper和上一次渲染的PageWrapper是完全不同的组件类型。
  • React会卸载旧的PageWrapper及其所有子组件(包括输入框),然后重新挂载新的PageWrapper和子组件。输入框被重新挂载后,自然会丢失焦点。

而直接使用Box组件时,Box是在组件外部定义的稳定组件类型,每次渲染都会复用,所以子输入框不会被卸载,焦点得以保留。

解决方案

将styled组件的定义移到组件外部,确保组件类型在每次渲染时保持稳定:

// 把PageWrapper移到EnterDetails组件外部定义
const PageWrapper = styled(Box)(({ theme }) => ({
  marginTop: theme.spacing(4),
  marginLeft: 'auto',
  marginRight: 'auto',
  textAlign: 'center',
  maxWidth: '800px',
}));

const EnterDetails = () => {
  const [formState, setFormState] = React.useState({
    firstName: '',
    lastName: '',
  });

  const onChange = (fieldName: string, newValue: any) => {
    const newState = { ...formState };
    newState[fieldName] = newValue.target.value;
    setFormState({ ...newState });
  };

  return (
        <PageWrapper>
          <BootstrapInput
            required={true}
            value={formState.firstName}
            onChange={(v) => onChange('firstName', v)}
          />
          <BootstrapInput
            required={true}
            value={formState.lastName}
            onChange={(v) => onChange('lastName', v)}
          />
        </PageWrapper>
  );
};

export default EnterDetails;

同理,如果你之前的FormRow、My_TextField等组件是在其他组件内部定义的,也需要确保它们的定义在组件外部,避免同样的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 09:55:31