Search code examples
javascriptreactjspopupreact-modal

How to pass state value from form to its child modal?


I am new to React and I'm not sure what would be the best approach to take.

I have a modal component to be displayed once the user fills out the values inside the form and click on Preview Voucher to print those values inside the modal.

I tried this code and below I have the Preview Voucher component with a constructor and events.

// PreviewVoucher.js
class PreviewVoucher extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      voucherNumber: "0",
      //
      addModalShow: false
    };

    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSelectChange = this.handleSelectChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleInputChange(e) {
    const target = e.target;
    const value = target.type === "checkbox" ? target.checked : target.value;
    const inputName = target.name;

    this.setState({
      [inputName]: value
    });
  }

  handleSelectChange(e) {
    this.setState({ labelSize: e.target.value });
  }

  handleSubmit(e) {
    e.preventDefault();
  }
}

And I want to render this form on the page that has a child component Modal Voucher

// PreviewVoucher.js
  render() {
    let addModalClose = () => this.setState({ addModalShow: false });
    return (
      <form onSubmit={this.handleSubmit}>
        <div>
          <p>Number of vouchers to create:</p>
          <input
            min="0"
            type="number"
            name="voucherNumber"
            value={this.state.voucherNumber}
            onChange={this.handleInputChange}
          />
        </div>
        <div>
          <h4>Message:</h4>
          <p>Some message</p>
          <ButtonToolbar>
            <Button onClick={() => this.setState({ addModalShow: true })}>
              Preview Voucher
            </Button>
            <ModalVoucher
              show={this.state.addModalShow}
              onHide={addModalClose}
            />
          </ButtonToolbar>
        </div>
        <input type="submit" value="Create voucher" />
      </form>
    );
  }

And this is the child component - Modal Voucher that would contain some text and would like to display the dynamic values from the Preview Voucher component inside the < Modal Body >.

// ModalVoucher.js
class ModalVoucher extends React.Component {
  render() {
    return (
      <Modal
        {...this.props}
        size="lg"
        aria-labelledby="contained-modal-title-vcenter"
        centered
      >
        <Modal.Header closeButton>
          <Modal.Title id="contained-modal-title-vcenter">
            Voucher preview
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <div>{/* update code here */}</div>
        </Modal.Body>
        <Modal.Footer>
          <Button onClick={this.props.onHide}>Close</Button>
        </Modal.Footer>
      </Modal>
    );
  }
}

Solution

  • How to render parent's state within child component

    You will need to pass the state value from PreviewVoucher into its child ModalVoucher as a prop.

    // PreviewVoucher.js
    render() {
      <ModalVoucher
        show={this.state.addModalShow}
        onHide={addModalClose}
        voucherNumber={this.state.voucherNumber}
      />
    }
    

    Then use it inside of ModalVouchers render method.

    // ModalVoucher.js
    render() {
      <Modal.Body>
        <div>{this.props.voucherNumber}</div>
      </Modal.Body>
    }
    

    I put together this sandbox showing this in action.

    Other suggestions

    After making this change, you might see this error in the console

    Warning: React does not recognize the `voucherNumber` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `vouchernumber` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
    

    This occurs because you are passing all the props directly into the Modal, which the underlying library passes to a DOM element.

    <Modal
      {...this.props}
    

    Because of this, I think it is usually a bad idea to directly forward all props and I prefer to pass specific props instead.

    <Modal
      show={this.props.show}
      onHide={this.props.onHide}