反应,为啥标签不为复选框触发 onChange?

Posted

技术标签:

【中文标题】反应,为啥标签不为复选框触发 onChange?【英文标题】:React, Why label not firing onChange for checkbox?反应,为什么标签不为复选框触发 onChange? 【发布时间】:2018-04-10 03:42:02 【问题描述】:

我觉得我已经做过一百万次了,但可能没有使用映射功能。我有一组活动,我正在创建复选框,代码如下(顺便说一下,__map 来自 Lodash):

<div className="activity-blocks">

    __map(this.state.activities, (activity, i) => 
        return (
            <div key= i  className="single-activity">
                <input id= `register-activity-$ i `  
                    type="checkbox" className="register-multiselect" 
                    name="main_activity" checked= !!activity.checked  
                    onChange= (e) => this.toggleActivity(e, activity.id) 
                />
                <label htmlFor= `register-activity-$ i ` > activity.name </label>
            </div>
         )
    )
 

我的onChange 处理程序如下所示:

toggleActivity(e, activityId) 
    let activities =  ...this.state.activities ;

    __forEach(this.state.activities, (activity) => 
        if (activityId === activity.id) 
            activity = __assign(activity,  checked: e.target.checked );
            return false;
        
    );

    this.setState( activities: activities );

处理程序工作得很好,主要是供参考。我的问题是标签没有触发处理程序。我只测试了没有标签的复选框输入,它触发了处理程序。有谁知道为什么标签不这样做?

更多信息:我以前遇到过这个问题,但通常结果是 id 不匹配或类似的简单问题。这次我不相信。


编辑 我已经从我的文件中删除了几乎所有的代码,并在下面合并了 Slawa 的答案,但它仍然没有按预期运行。这是包含所有导入、类结构和所有不必要的代码的代码,包括样式:

import React from 'react';
import __forEach from 'lodash/forEach';
import __assign from 'lodash/assign';
import __map from 'lodash/map';

export default class RegisterActivities extends React.Component 
    constructor(props) 
        super(props);

        this.state = 
            activities: [
                
                    id: 1,
                    name: 'label-1'
                ,
                
                    id: 2,
                    name: 'label-2'
                ,
                
                    id: 3,
                    name: 'label-3'
                
            ],
        
    

    toggleActivity(e, activityId) 
        const  activities  = this.state;

        const updated = activities.map((activity) => 
            if (activity.id === activityId)
                return 
                    ...activity,
                    checked: e.target.checked
                
            

            return activity;
        );
    

    render() 
        return (
            <div>
                <p>what do you do?</p>
                <div>
                
                    __map(this.state.activities, (activity, i) => 
                        return (
                            <div key= i >
                                <input id= `register-activity-$ i `  
                                    type="checkbox" 
                                    name="main_activity" checked= !!activity.checked  
                                    onChange= (e) => this.toggleActivity(e, activity.id) 
                                />
                                <label htmlFor= `register-activity-$ i ` > activity.name </label>
                            </div>
                        )
                    )
                
                </div>
            </div>
        );
    

编辑2 我在玩,决定用onClick 替换onChange,现在我可以点击实际的复选框来点击toggleActivity。有人知道为什么 onChange 没有触发吗?

【问题讨论】:

【参考方案1】:

找到了我的问题的答案on this SO post。事实证明,我的标签的 onClick 事件(在 html/React 的幕后,不是我自己编码的)正在冒泡到父级而不是处理输入。我需要通过 onClick= e =&gt; e.stopPropagation() 告诉标签停止传播。

【讨论】:

好吧,你忽略了 React 对复选框更改的处理实际上是基于点击的 hack,“以规范浏览器差异”,所以如果你有任何无害点击 preventDefault 的祖先,它干扰 React 的复选框更改黑客。【参考方案2】:

我认为,您的 toggleActivity 函数有错误,活动是数组并尝试将其转换为对象,您可以在这里找到我的完整解决方案:https://codesandbox.io/s/wqyolw50vl

  toggleActivity(e, activityId) 
    const  activities  = this.state;

    const updated = activities.map( activity => 
      if (activity.id === activityId)
        return 
          ...activity,
          checked: !activity.checked
        
      

      return activity;
    )

    this.setState( activities: updated );
  

【讨论】:

感谢您的收获。出于某种原因,我仍然无法弄清楚是什么阻止了我的标签单击输入。 toggleActivity 根本不会触发。将调试器放在函数的顶部不会做任何事情。 在您的示例中,您没有正确更新本地状态,因此您的复选框没有新的选中值,codesandbox.io/s/wqyolw50vl - 工作示例 我刚刚在我的文件中使用您的示例进行了编辑,删除了所有其他代码,但它仍然不适合我。 编辑同样出于绝望,我将您在 App 类中的解决方案完全复制到我的解决方案中,但仍然没有触发任何点击。【参考方案3】:

我遇到了同样的问题。这让我发疯,但我和我的团队发现他们告诉你在文档中使用的htmlFor 一定有一些错误。我们从我们的复选框属性中删除了它,现在它按预期运行,没有奇怪的副作用。我正在打开一个GH问题。稍后我将使用链接/更新进行编辑

无效的代码:

<label htmlFor=`$label-checkbox` className=checked ? "checked data-filter-checkbox" : "data-filter-checkbox" >
  <input id=`$label-checkbox` type="checkbox" onChange=(event) => handleCheck(event) value=label name=label checked=checked />
  label
  <span>count</span>
</label>

我现在使用的代码:

<label className=checked ? "checked data-filter-checkbox" : "data-filter-checkbox" >
  <input id=`$label-checkbox` type="checkbox" onChange=(event) => handleCheck(event) value=label name=label checked=checked />
  label
  <span>count</span>
</label>

【讨论】:

太棒了。我正在处理的项目是唯一需要动态创建复选框的项目,所以我很想知道它的进展。谁知道呢,也许这只是 React 15 的错误,在 16 中修复了。

以上是关于反应,为啥标签不为复选框触发 onChange?的主要内容,如果未能解决你的问题,请参考以下文章

反应复选框:event.preventDefault() 中断 onChange 函数 - 为啥?

为啥不为不可见组件触发 OnUpdate [重复]

如何为javascript复选框提供onchange事件?

有人遇到过iCheck插件的事件触发问题么

动态更改复选框不会触发 onChange?

复选框 onchange 触发两次