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
相关产品推荐
相关产品推荐

