Work out which component an event is coming from

I have a react component for a checkbox that is used in multiple components.

const Checkbox = (props) => {
    return (
        <Col>
            <div className="switch">
                <input id={props.value} type="checkbox" value={props.value} className="visually-hidden" onChange={props.handleChange}/>
                <label htmlFor={props.value} className="switch-label checkbox-label text-center">{props.label}</label>
            </div>
        </Col>
    )
}

export default Checkbox

I want to have a single function that handles the change from this component but work out the parent component that called it so I can update the correct value in my state.

For example, I have an Attendees component that uses my checkbox component:

export default class Attendees extends Component {
    state = {
        attendees: [
            {id: 1, value: 500, label: "0 - 500", checked: false},
            {id: 2, value: 1500, label: "500 - 1.5k", checked: false},
            {id: 2, value: 5000, label: "1.5k - 5k", checked: false},
            {id: 2, value: 10000, label: "5k - 10k", checked: false},
            {id: 2, value: 25000, label: "10k - 25k", checked: false},
            {id: 2, value: 50000, label: "25k - 50k", checked: false},
            {id: 2, value: 1000000, label: "50k+", checked: false},
        ]
    }

    render() {
        return (
            <div>
                <Row>
                    <Col>
                        <h4 className="ui centered question-header text-center">HOW MANY ATTENDEES DO YOU EXPECT?</h4>
                    </Col>
                </Row>
                <Row>
                    {
                        this.state.attendees.map((number) => {
                            return (<Checkbox name="attendees" key={number.value}
                                           handleChange={this.props.handleChange} {...number} />)
                        })
                    }
                </Row>
            </div>
        )
    }
}

When my handleChange function gets fired I want to know if it came from the attendees component so I can update my attendee’s value in my state, currently I have this as my handleChange function which only updates the event_format on my state. Is this possible?

constructor() {
        super();
        this.state = {
            event_format: [],
            attendees: 0,
        }

        this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
    }

    handleCheckboxChange(event) {
        const target = event.target;
        let value = target.value;
        let eventFormatsCopy = this.state.event_format;
        if (target.checked) {
            eventFormatsCopy.push(value)
            this.setState({
                event_format: eventFormatsCopy
            })
        } else {
            const index = eventFormatsCopy.indexOf(value)
            eventFormatsCopy.splice(index, 1);
            this.setState({
                event_format: eventFormatsCopy
            })
        }
        console.log(this.state)
    }

2 thoughts on “Work out which component an event is coming from”

  1. The React way would involve passing props down each component, as an alternative (quicker) way you could use a distinct attribute on the parent element you can then use to back-reference from the current / target node from the event e.g.

    <div id="attendees">
      <Row>{ /* render check boxes */ }</Row>
    </div>
    

    Then you can traverse the DOM back up to detect if that particular ID is part of the ancestry. For illustration, if you are using jQuery as an example:

    const isAttendee = $(event.target).closest('#attendees').length > 0;
    

    If you have many different scenarios and you need something a bit more flexible, maybe a custom attribute with a type would be better e.g. data-component="attendees".

    Reply
  2. I realize this has already been answered, but the accepted answer is A Bad Idea™ in my opinion, so I offer these simple alternatives.

    1. Pass a different handler:

      <Checkbox onChange={this.handleAttendeesChange} />
      <Checkbox onChange={this.handleOtherStuff} />
    

    Or if you really need to use a single handler for whatever reason, you can…


    2. Use the input’s name:

    If you pass the name prop through to the input you can test target.name in your change handler:

    const Checkbox = (props) => {
        return (
            <Col>
                <div className="switch">
                    <input
                        name={props.name} {/* pass the name prop to the input */}
                        id={props.value}
                        type="checkbox"
                        value={props.value}
                        className="visually-hidden"
                        onChange={props.handleChange}
                    />
                    <label htmlFor={props.value} className="switch-label checkbox-label text-center">{props.label}</label>
                </div>
            </Col>
        )
    }
    
    export default Checkbox
    
    // unchanged from your example
    <Checkbox
        name="attendees"
        key={number.value}
        handleChange={this.props.handleChange}
        {...number}
    />
    
    handleCheckboxChange(event) {
       if (event.target.name === 'attendees') {
          // do attendee stuff
       }
       else {
          // do other stuff
       }
    }
    
    Reply

Leave a Comment