React Typescript subcomponent

510 views Asked by At

I'm trying to make a React Component with Subcomponents in the style of: https://react-bootstrap.github.io/components/cards/ which should render one component on the left and one on the right

<MyComponent>
   <MyComponent.Left>
      foo
   </MyComponent.Left>
   <MyComponent.Right>
      bar
   </MyComponent.Right>
</MyComponent>

My basic strategy has been to create something like this:

function MyComponent(props:PropsWithChildren):JSX.Element{
var leftComponent = ???;
var rightComponent = ???;
return 
(<div>
   <div className="this-goes-on-the-right">leftComponent</div>
   <div className="this-goes-on-the-left">rightComponent</div>
</div>);
}

function MyComponent.Left = function MyComponentLeft(props:PropsWithChildren){
   return (<div>props.children</div>);
}
function MyComponent.Right = function MyComponentRight(props:PropsWithChildren){
   return (<div>props.children</div>);
}

But I'm lost as to how to figure out which of the children passed to MyComponent is the MyComponent.Left and which is the MyComponent.Right. How can I do this in typescript?

2

There are 2 answers

1
Asplund On

They are using Object.assign to assign "sub components".

const Card: BsPrefixRefForwardingComponent<'div', CardProps> = React.forwardRef<
  HTMLElement,
  CardProps
>(
  (
    {
      props
    },
    ref,
  ) => {
    return (
      <Component
        ...
      </Component>
    );
  },
);

Card.displayName = 'Card';
Card.propTypes = propTypes;
Card.defaultProps = defaultProps;

export default Object.assign(Card, {
  Img: CardImg,
  Title: CardTitle,
  Subtitle: CardSubtitle,
  Body: CardBody,
  Link: CardLink,
  Text: CardText,
  Header: CardHeader,
  Footer: CardFooter,
  ImgOverlay: CardImgOverlay,
});

Source

2
Kia Kaha On
  1. There are multiple ways probably to approach what you are aiming at. The most basic could be to set some names to the components and check each child for it's name (react docs) but I would not recommend this.

  2. Rather you should style your MyComponent.Left and MyComponent.Right properly so they are shown the desired way no matter of the order they are passed to the MyComponent's children.

Sketch up of what I mean:

function MyComponent(props:PropsWithChildren):JSX.Element{
  return (
    <div>{props.children}</div>
  );
}

function MyComponent.Left = function MyComponentLeft(props:PropsWithChildren){
   return (<div className="this-goes-on-the-left">props.children</div>);
}
function MyComponent.Right = function MyComponentRight(props:PropsWithChildren){
   return (<div className="this-goes-on-the-right">props.children</div>);
}

The implementation of the classes which style the nested component could be based on flex-box rules, or floating or whatever suits your use case.

  1. One more option which is slightly off your example but might be useful is to pass the components as props, not children like:
function MyComponent(props:PropsWithChildren):JSX.Element{
  return (
    <div>
       <div className="this-goes-on-the-right">{props.right}</div>
       <div className="this-goes-on-the-left">{props.left}</div>
    </div>
  );
}