Search code examples
reactjstypescriptstaticcompositionsubcomponent

REACT - Composition of a component with subcomponents in TypeScript


react-bootstrap let you create a modal component with:

<Modal>
    <Modal.Header>
        <Modal.Title>Modal heading</Modal.Title>
    </Modal.Header>
    <Modal.Body>
        <h4>Text in a modal</h4>
    </Modal.Body>
    <Modal.Footer>
        <Button>Close</Button>
    </Modal.Footer>
</Modal>

I want to do a composition of the modal component and create a MyModal component:

import * as React from 'react';
import { Modal, ModalBody, ModalProps } from 'react-bootstrap';

interface Props extends ModalProps {
  plain?: boolean;
}

class MyModal extends React.Component<Props> {

  static Body: typeof ModalBody;

  constructor(props: Props) {
    super(props);
  }

  render() {
    const { plain, ...other } = this.props;
    return (
      <Modal className={plain ? 'plain' : ''} {...other} />
    );
  }
}

export default MyModal;

but if I use it like this:

import MyModal from './MyModal';

...

render() {
  return (
    <MyModal plain={true}>
      <MyModal.Body>
        <p>Hello!</p>
      </MyModal.Body>
    </MyModal>
  );
}

I get the following error:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Any idea what's going wrong?


Solution

  • static Body: typeof ModalBody;
    

    is undefined, so it cannot be used as <MyModal.Body>.

    In case it's inherited from wrapped component, it should be:

    static Body = ModalBody;
    

    Where Body type is inferred.

    This problem could be avoided by using TypeScript strict (strictNullChecks) compiler option.