Home > Software design >  Dynamic props depending on other prop
Dynamic props depending on other prop

Time:11-07

I have this component, which is just a title and an icon on the right, and the container of this component can be a button, an anchor element, or a Link element from react-router-dom.

component

I was able to create the container dynamically like so:

import { Link } from "react-router-dom";
// ....
type Props = {
  as: "button" | "a" | "Link";
  title: string;
  icon: string;
};
const ActionItem: React.FC<Props> = ({ as, title, icon }: Props) => {
  const Container = styled(as === "Link" ? Link : as)`
    display: flex;
    // ...
  `;

  return (
    <Container>
      {title}
      <div style={{ flex: 1 }} />
      {icon}
    </Container>
  );
};

But now what I'm trying to accomplish is to have dynamic props depending on the as prop, so, for example, if the as prop is equal to Link show all the props for the Link component like to, replace, etc. Or if the as is equal to a, show all the props for the anchor element like href, target, etc.

<ActionItem 
  as="Link"
  to="/profile"
  ...
/>

I tried this but it didn't work, mostly because in the example they are using native HTML elements, and I need to use those (button and anchor element) plus the Link component.

I don't know if what I'm trying to find is possible or if it is another possible solution for this?

BTW, here's a code sandbox with the code

Edit: Here's the sandbox of the code working as it should

CodePudding user response:

You should use a so-called discriminating union (or discriminative union). Let's use the as prop as the discriminator.

You already have

type Props = {
    as: "button" | "a" | "link"
}

Break that out to

type Props = 
    { as: "button" } | 
    { as: "a" } | 
    { as: "Link" }

Now simply add to each part of the union what props you expect there for Typescript to deduce what other properties are possible, given the discriminator prop as:

type Props = 
    { as: "button" } & ButtonProps | 
    { as: "a" } & AProps |
    { as: "Link" } & LinkProps

Done!

By the way, I used made up names for the complementary props you need, you have to look up the names of the types you want to intersect your discriminator prop with yourself.

  • Related