React返回const JSX与函数形式时输入框无法保持焦点的原因
React嵌套定义内联组件导致输入框重渲染失焦原因
核心问题本质
每次MyInput组件重渲染时,内部定义的Input箭头函数会生成全新的函数引用,React的diff算法会将其识别为完全不同的组件类型,触发旧DOM卸载、新DOM挂载的完整流程,输入框作为被卸载重建的DOM节点自然会丢失焦点。
两种写法的差异对比
- 初始写法不存在问题的原因:
代码中const Input = <input {...props} />是直接创建类型为原生input标签的React元素,重渲染时React识别到元素类型未发生变化,只会对比更新属性,不会重建DOM节点,输入框焦点可以正常保留。function MyInput(props) { const Input = <input {...props} /> return <Label>{Input}</Label> } - 修改后写法触发问题的逻辑:
将Input定义为MyInput内部的箭头函数后,每次MyInput执行重渲染,都会重新创建一个全新的Input函数。JSX中<Input />的组件类型指向这个每次都会变化的函数引用,按照React的diff规则:组件类型引用变化时,会直接销毁该组件对应的全部子DOM节点,重新挂载新组件的节点。旧输入框被销毁、新输入框被挂载,新节点默认不会继承旧节点的焦点状态,就会出现输入中断、必须重新点击激活的现象。// 问题代码 function MyInput(props) { // 每次重渲染这里的Input都是全新的函数引用 const Input = () => <input {...props} /> return ( <Label> <Input /> </Label> ) } - 防抖场景下问题凸显的原因:
防抖逻辑会在用户停止输入时触发重渲染,刚好卡在两次输入的间隙触发DOM重建,用户下一次输入前就会直接感知到输入框失焦。
修复方案
不要在函数组件内部嵌套定义子组件,两种可落地的修改方式:
- 直接在返回的JSX中书写原生
input元素,或直接将inputReact元素赋值给变量,不包装成内联函数组件 - 如果需要抽离组件复用逻辑,将
Input组件移动到MyInput外层作用域定义,保证组件引用在多次重渲染中保持稳定,通过props传参即可// 修复后:Input定义在组件外层,引用稳定 const Input = (props) => <input {...props} /> function MyInput(props) { return ( <Label> <Input {...props} /> </Label> ) }
内容的提问来源于stack exchange,提问作者Booger
相关产品推荐
相关产品推荐

