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 coming 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?
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 });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With