React文本输入框二次更新后崩溃问题排查
React输入框onInput触发两次后崩溃的问题分析与解决
你的代码核心问题在于在setState的函数式更新中异步访问了已被React回收复用的合成事件对象。
React为了性能优化,会对合成事件对象进行池化处理——事件回调执行完毕后,React会清空事件对象的属性,并将currentTarget设为null,然后把这个对象放回事件池供后续事件复用。你使用了setTxt(() => e.currentTarget.value)这种函数式更新,而setState的更新函数是异步执行的,当它真正运行时,原来的事件对象e已经被回收,e.currentTarget变成了null,此时读取value自然会抛出Cannot read properties of null (reading 'value')的错误。
修复方案
方案一:提前提取值,同步更新状态(推荐)
先把输入框的值从事件对象中提取出来,再传给setState,避免异步访问回收后的事件对象:
import { useState } from "react"; export function App() { const [txt, setTxt] = useState(""); return ( <input value={txt} onInput={(e) => { const inputValue = e.currentTarget.value; setTxt(inputValue); }} /> ); }
方案二:函数式更新结合值提取(适合依赖旧状态的场景)
如果你的更新逻辑需要依赖之前的状态值,可以先提取当前输入值,再在函数式更新中使用:
import { useState } from "react"; export function App() { const [txt, setTxt] = useState(""); return ( <input value={txt} onInput={(e) => { const inputValue = e.currentTarget.value; setTxt(prevTxt => inputValue); // 示例:如果需要拼接旧值,可写成 prevTxt + inputValue }} /> ); }
方案三:禁用事件池化(React 16及以前可用,17+不推荐)
在React 16及更早版本中,可以调用e.persist()阻止事件对象被池化回收,但React 17之后该方法已不再生效(因为React 17改变了事件委托机制,不再池化事件对象),所以不推荐使用:
// React 16及以前版本可用 <input value={txt} onInput={(e) => { e.persist(); setTxt(() => e.currentTarget.value); }} />
总结
优先选择方案一或方案二,通过提前提取输入值的方式,避免异步访问已被回收的合成事件对象,这是最稳妥且符合React设计规范的做法。
内容的提问来源于stack exchange,提问作者baitendbidz
相关产品推荐
相关产品推荐

