Nick Kinlen
Nick Kinlen

Reputation: 1406

React: Pass function to child component as props, call onClick in child component

I have a React class based component. I want to pass a child component a function from this parent component. When the child component does an onClick event I will call the parent's function. Then, I want to update state in the parent component based on what element was clicked in the child component.

QuestionsSection is the parent component with state:

class QuestionsSection extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isVisible: "option1"
    };
  }

  handleOptionChange = e => {
    this.setState({
      isVisible: e.target.value
    });

    alert("function called");
  }

  render() {
    return (
      <div>
        <QuestionsItems
          isVisible={this.state.isVisible}
          handleOptionChange={() => this.handleOptionChange()}
      </div>
    );
  }
}

QuestionsItems is the child component that is stateless/function component:

const QuestionsItems = (props) => {
    return (
      <div>
        <Container className="d-flex flex-md-row flex-column justify-content-center">
          <Row className="d-flex flex-md-column flex-row order-1 justify-content-center">
            <Col className={props.isVisible === 'option1' ? 'highlighted' : 'questions-section'}>
              <label className="cursor-pointer" onClick={props.handleOptionChange}>
                <input
                  type="radio"
                  value="option1"
                  checked={props.isVisible === 'option1'}
                  onChange={props.handleOptionChange}>
                </input>
                <Col xs={{span: 12}}>
                  <img src={questions1} alt="pic" height="50"/>
                </Col>
                <p>Ask a question</p>
              </label>
            </Col>
            <Col className={props.isVisible === 'option2' ? 'highlighted' : 'questions-section'}>
              <label className="cursor-pointer" onClick={props.handleClick}>
                <input
                  type="radio"
                  value="option2"
                  checked={props.isVisible === 'option2'}
                  onChange={props.handleOptionChange}>
                </input>
                <Col xs={{span: 12}}>
                  <img src={questions2} alt="pic" height="50"/>
                </Col>
                <p>Vote on everything</p>
              </label>
            </Col>
        </Row>
        <Row className="d-flex flex-md-column flex-row image-container order-md-2 order-3 justify-content-center">
          <Col
            xs={{span: 12}}
            className="featured-question order-lg-2 order-3">
            {
              props.isVisible === 'option1' ?
                <Col xs={{span: 12}}>
                  <img src={questionsBig1} alt="featured image"/>
                </Col>
              : ""
            }

            {
              props.isVisible === 'option2' ?
                <Col xs={{span: 12}}>
                    <img src={questionsBig2} alt="featured image" />
                </Col>
              : ""
            }            
          </Col>
        </Row>
        </Container>
      </div>
    );
}

This component has a lot of markup/Bootstrap syntax. Ignore that, it's just two elements that both have an onClick event. The third part is just ternary logic that displays either the first or second element based on value.

The problem I am having lies with updating this.state in the parent component. e.target.value is undefined. How can I update the QuestionsSection's (the parent component) state based on which element the child component clicks? Is it a matter of passing the value on the clicked element in the child component back to the parent component?

Here's the error: enter image description here

Upvotes: 1

Views: 6538

Answers (2)

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38757

In the parent, you aren’t passing any arguments to the handler where setState() is being called currently. You need to pass the value passed from the child to the handler, in this the change event. Before making the following change, try logging out e in handleOptionChange, you will see it's undefined. Try the following:

handleOptionChange={(e)=> this.handleOptionChange(e)}

You can also shorten this:

handleOptionChange={this.handleOptionChange}

Hopefully that helps!

Upvotes: 1

Alejandro Garcia Anglada
Alejandro Garcia Anglada

Reputation: 2403

You need to pass handleOptionChange as a callback of handleOptionChange prop

Change this:

handleOptionChange={() => this.handleOptionChange()}

to

handleOptionChange={this.handleOptionChange}

This happens because the handler expects an event that is not passed to the function.

Upvotes: 1

Related Questions