0

I create a dynamic button (Comp) in React TypeScript. "Comp" can be a button, anchor, or Link (React Router). I got a problem with the type having no properties in common with the type 'IntrinsicAttributes'.

type ButtonProps = {
  href?: string;
  to?: string;

  children: ReactNode;
};

function Button(props: ButtonProps) {
  const { href, to, children } = props;

  let Comp = 'button';
  if (href) Comp = 'a';
  if (to) Comp = 'Link';

  const compProps = { 
    href,
    to,
  };

  return <Comp {...compProps}>{children}</Comp>;
}

Here is the problem:

Type '{ children: ReactNode; href: string | undefined; to: string | undefined; }' has no properties in common with type 'IntrinsicAttributes'.ts(2559).

I researched some pics in StackOverflow but it's not my case.

Gregoire Ducharme
  • 1,095
  • 12
  • 24
Huy Pham
  • 17
  • 4

3 Answers3

1

If you want to pass elements as strings, you can use React.createElement for dynamic props with those elements.

Note that Link is not a standard HTML element, so we cannot pass it as a string like button and a.

import React, { ReactNode, ElementType } from 'react'
import { Link } from 'react-router-dom'

type ButtonProps = {
  href?: string;
  to?: string;

  children: ReactNode;
};

function Button(props: ButtonProps) {
  const { href, to, children } = props;

  let comp: ElementType | typeof Link = 'button';
  if (href) comp = 'a';
  if (to) comp = Link;

  const compProps = { 
    href,
    to,
  };

  return React.createElement(comp, compProps, children);
}

Playground

Gregoire Ducharme
  • 1,095
  • 12
  • 24
Nick Vu
  • 14,512
  • 4
  • 21
  • 31
  • I appreciate your support! I tried and it works. – Huy Pham Jan 06 '23 at 02:19
  • If you find my answer useful, could you help me to mark my answer as your solution? that will give me a little support as well as our community. Thank you!~ @HuyPham – Nick Vu Jan 06 '23 at 02:35
0

You can't use a string as a JSX constructor: Comp is not a valid tag.

You can rewrite your code like so:

type ButtonProps = {
  href?: string;
  to?: string;

  children: ReactNode;
};

function Button(props: ButtonProps) {
  const { href, to, children } = props;

  if (href) return <a href={href}>{children}</a>;
  if (to) return <Link to={to}>{children}</Link>;
  return <button>{children}</button>;
}
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
0

Based on Nick Vu's answer. I solved the problem.

import React, { ElementType, ReactNode } from 'react';
import { Link, LinkProps } from 'react-router-dom';

type ButtonProps = {
  href?: string;
  to?: string;
  children: ReactNode;
};

type CompType = ElementType | LinkProps;

function Button(props: ButtonProps) {
  const { href, to, children } = props;

  let Comp: CompType = 'button';
  if (href) Comp = 'a';
  if (to) Comp = Link;

  const compProps = {
    href,
    to,
  };

  return <Comp {...compProps}>{children}</Comp>;
}

export default Button;
Huy Pham
  • 17
  • 4