给力星

Web Developer

React中对Button设置disabled后仍可点击的问题研究

在工作中参与的一个 Web IM 项目主要是基于 React 技术栈来构建,遇到过这样一个问题:对 Button 设置 disabled,但是仍然可以点击来触发执行点击事件。一般情况下,对 Button 设置 disabled 后,确实不可再点击了,那在 React 中为何会存在这个问题?

问题描述

问题起源于某个功能面板,如果连续点击多次确定按钮,会导致发起多次重复请求的情况。

为防止多次请求的情况发生,通常有如下三种解决方法:

  1. 更改逻辑,点击后立即让面板消失,也就无法多次点击了
  2. 通过设置变量,判断、控制是否可以点击
  3. 对 Button 设置 disabled 属性,让其不可点击

第1个方案不适合所有情况,第2个方案多了一些额外操作,第3个方案则比较理想,毕竟设置 disabled 属性后,Button 会呈现出相应的样式,用户便可感知该 Button 不可再点击了。

但是在若采用第3个方案具体实践时,使用代码 ReactDOM.findDOMNode(this.refs.forwardBtn).disabled = true; 对 Button 设置 disabled 属性,却发现该 Button 仍然可以点击,如下图所示:

Button的子元素绑定了点击事件Button的子元素绑定了点击事件

一般情况下,对 Button 设置 disabled 后,确实不可再点击了,那在 React 中为何会存在这个问题?

问题原因

这个问题主要是因为 React 采用了不同的事件处理机制,其采用了合成事件,有个不同之处是,如果我们对 Button 绑定了一个点击事件,那么其子元素中也会绑定该点击事件。因此,如果 Button 中存在子元素,那么该子元素仍然是可以点击并触发点击事件的,如下图所示:

Button的子元素绑定了点击事件Button的子元素绑定了点击事件

解决方法

其实 Button 组件里是有考虑到这个情况,其给 button 元素绑定的事件如下:

let onClick1 = type === 'disabled' ? function() {} : onClick;

也就是说,如果所以我们从组件传入 disabled 属性,那么它会给 Button 绑定一个空函数,从而其表现是正确的。

但若是直接设置 disabled 属性,而 button 中含有子节点的话,该子节点还是会触发点击事件。

大象 Web 的这个 Button 组件使用的是 Ant Design 这个开源组件,翻看了其 Github 上的讨论,发现有关于这个问题的讨论–Upload 组件支持 disabled 属性,并且给出了一个漂亮的解决方法:可以利用 pointer-events,让设置为 disabled 的 Button 的子元素不可点击,即可解决这个问题,代码如下

&[disabled] {
> * {
    pointer-events: none;
}

参考资料