Home > OS >  Pass Components to another component when not giving specified typescript types
Pass Components to another component when not giving specified typescript types

Time:09-04

Encountered a problem where I do not know how to work-around this in strongly typed variables.

So, I have a bunch of icons as components and an icon wrapper component. Wrapper component has themeMode variable in order to determine in what color should icon be displayed. Hence, every icon needs themeMode variable, which is mandatory. However, I cannot think of the workaround when I have to pick an icon based of switch case scenario.

Here is the code for better understandment: This component gets an id from a url and displays certain icon component. However, every switch cases returning statements are underlined in red. That's because icon component NEEDS that themeMode variable. However, I'm giving it on iconWrapper component.

const CollectionEnd = () => {
  const { id } = useParams();

  const getEndSvg = () => {
    switch (id) {
      case "bags":
        return <Bags />;
      case "shoes":
        return <Shoes />;
      case "men":
      case "women":
        return <Clothes />;
      case "hats":
        return <Hats />;
      default:
        return <Clothes />;
    }
  };

  return (
    <Container>
      <IconWrapper Component={getEndSvg()} />
    </Container>
  );
};

Icon wrapper component:

const IconWrapper: React.FC<{
  Component: React.FC<{
    themeMode: string;
    style?: React.CSSProperties;
    isOpened?: boolean;
  }>;
  isOpened?: boolean;
  otherProps?: React.ReactNode;
  style?: React.CSSProperties;
}> = ({ Component, style, isOpened, ...otherProps }) => {
  const themeMode = useAppSelector(selectThemeMode);
  return (
    <div style={style}>
      <Component {...otherProps} isOpened={isOpened} themeMode={themeMode} />
    </div>
  );
};

Here is one of the icon component's emitted code:

const Bags: React.FC<{ themeMode: string }> = ({ themeMode }) => {
  return (
    <svg
      id='svg'
      xmlns='http://www.w3.org/2000/svg'
      width='200'
      height='200'
      viewBox='0, 0, 400,400'
      version='1.1'
    >
-----> ...emitedCode <------

Yes, I can simply make themeMode as not required, but that is not good. Also, I can wrap all switch case scenarios in IconWrapper. However, it looks repetitive.

CodePudding user response:

One approach could be to change IconWrapper to accept React.ElementType and to pass the Icon component rather than an instantiated JSX.Element. This would let you delegate instantiating the Icon component to IconWrapper, which has the themeMode available.

In practice, this would look something like:

const CollectionEnd = () => {
  const { id } = useParams();

  const getEndSvg = () => {
    switch (id) {
      case "bags":
        return Bags;
      case "shoes":
        return Shoes;
      case "men":
      case "women":
        return Clothes;
      case "hats":
        return Hats;
      default:
        return Clothes;
    }
  };

  return (
    <Container>
      <IconWrapper Component={getEndSvg()} />
    </Container>
  );
};

const IconWrapper: React.FC<{
  Component: React.ElementType<{
    themeMode: string;
    style?: React.CSSProperties;
    isOpened?: boolean;
  }>;
  isOpened?: boolean;
  otherProps?: React.ReactNode;
  style?: React.CSSProperties;
}> = ({ Component, style, isOpened, ...otherProps }) => {
  const themeMode = useAppSelector(selectThemeMode);
  return (
    <div style={style}>
      <Component {...otherProps} isOpened={isOpened} themeMode={themeMode} />
    </div>
  );
};
  • Related