javascript - React, Why label not firing onChange for checkbox? - Stack Overflow

admin2025-03-17  1

I feel like I've done this a million times, but possibly not with a mapping function. I have a collection of activities that I'm creating checkboxes with the code is as follows (__map is ing from Lodash by the way):

<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>
         )
    })
 }

My onChange handler looks like this:

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 });
}

The handler works just fine, it's for reference mostly. My issue is that the label is not firing the handler. I have tested only the checkbox input with no label and it fires off the handler. Does anyone know why the label is not doing so?

More info: I've had this problem before but it usually turns out to be an id mismatch or something simple like that. I don't believe so this time.


Edit I have deleted pretty much all the code from my file and incorporated Slawa's answer below and it still doesn't behave as expected. Here is the code including all imports, the class structure and all unnecessary code stripped out, including styles:

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>
        );
    }
}

Edit2 I'm playing around and decided to replce onChange with onClick and now I'm able to click the actual checkbox to hit toggleActivity. Does anyone have an idea on why onChange isn't firing?

I feel like I've done this a million times, but possibly not with a mapping function. I have a collection of activities that I'm creating checkboxes with the code is as follows (__map is ing from Lodash by the way):

<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>
         )
    })
 }

My onChange handler looks like this:

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 });
}

The handler works just fine, it's for reference mostly. My issue is that the label is not firing the handler. I have tested only the checkbox input with no label and it fires off the handler. Does anyone know why the label is not doing so?

More info: I've had this problem before but it usually turns out to be an id mismatch or something simple like that. I don't believe so this time.


Edit I have deleted pretty much all the code from my file and incorporated Slawa's answer below and it still doesn't behave as expected. Here is the code including all imports, the class structure and all unnecessary code stripped out, including styles:

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>
        );
    }
}

Edit2 I'm playing around and decided to replce onChange with onClick and now I'm able to click the actual checkbox to hit toggleActivity. Does anyone have an idea on why onChange isn't firing?

Share Improve this question edited Oct 28, 2017 at 23:35 Martavis P. asked Oct 28, 2017 at 19:24 Martavis P.Martavis P. 1,8383 gold badges30 silver badges49 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

The answer to my question was found on this SO post. It turns out that my label's onClick event (behind the scenes of html/React, not coded myself) was bubbling up to the parent instead of handling the input. I needed to tell the label to stop propagating by doing onClick={ e => e.stopPropagation() }.

I think, you have an error in your toggleActivity function, activities is array and try to convert it to object, you can find my full solution of this problem here: 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 });
  }

I was having this same issue. It was driving me mad, but what my team and I discovered is there must be some bug with the htmlFor they tell you to use in the docs. We removed it from our checkbox prop and now it functions as expected with no weird side effects. I'm opening up a GH issue on it. I'll edit later with the link/update

Code that didn't work:

<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>

Code I'm using now:

<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>
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1742172476a200573.html

最新回复(0)