外观
React 的事件和原生事件有什么差别?
React 把监听委托到根容器,再用合成事件对象把回调分发到组件上。写在 JSX 里的 onClick 不是直接 addEventListener 在那个 DOM 节点上。混用两套时,原生监听会先看到事件。
原理
合成事件抹平浏览器差异,并复用事件对象。回调里的 event.target 仍是实际点中的 DOM。需要当前组件对应的节点时用 event.currentTarget。
React 17 起委托到渲染根而不是 document,方便一个页面里放多个 React 根。停止冒泡只影响 React 这棵树上还没执行的监听。绑在 DOM 上的原生监听如果在 React 根之外,或在目标到根的路径上更早执行,stopPropagation 挡不住它。
不要在回调里异步使用事件对象的字段。React 17 之后事件池取消了,字段还能读,但异步逻辑更应该先取出需要的值,或使用原生事件。
列表项不要在渲染时给每个子节点绑一份全新的重逻辑。稳定的回调配数据 id,或者把监听放在父级再看 target。见 事件机制。
追问
为什么有时 onClick 不触发?
原生监听在捕获阶段调用了 stopPropagation,事件到不了 React 根。另一类情况是元素被盖住,点到的不是你以为的节点。先看 target,再看谁提前停了冒泡。
绑定方式有哪些?
JSX 里写 onClick={fn}。类组件要把 this 绑好:构造函数里 bind,或类字段箭头函数。每次 render 都 bind 会制造新函数,可能让子组件多渲染一次。
易错点
- 把合成事件的
stopPropagation当成能停掉所有原生监听。 - 在渲染函数里写
onClick={handle()},挂载时就执行了。 - 给禁用按钮的点击依赖
click。disabled的按钮不会触发点击,要用外层元素或看具体控件的事件。