Как набрать компонент React, который принимает другой компонент в качестве реквизита и все реквизиты этого компонента? - PullRequest
0 голосов
/ 10 апреля 2020

У меня есть компонент React <Wrapper>, который я могу использовать следующим образом:

// Assuming Link is a component that takes a `to` prop: <Link to="/somewhere">label</Link>
<Wrapped as={Link} to="/somewhere">label</Wrapped>

Если не пропущен as проп, он примет <a>. Но если компонент передается в as, все реквизиты, которые действительны для этого компонента, теперь также должны быть действительными реквизитами Wrapped.

Есть ли способ ввести это в TypeScript? В настоящее время я размышлял по этому поводу:

type Props<El extends JSX.Element = React.ReactHTMLElement<HTMLAnchorElement>> = { as: El } & React.ComponentProps<El>;
const Wrapped: React.FC<Props> = (props) => /* ... */;

Однако я не уверен, являются ли JSX.Element и / или React.ComponentProps соответствующими типами здесь, и это не компилируется, потому что El может не будет передано ComponentProps. Какие бы там были правильные типы, и возможно ли что-то подобное?

Ответы [ 3 ]

1 голос
/ 13 апреля 2020

Вам нужны следующие типы: ComponentType и ElementType.

import React, { ComponentType, ElementType, ReactNode } from 'react';

type WrappedProps <P = {}> = { 
  as?: ComponentType<P> | ElementType
} & P

function Wrapped<P = {}>({ as: Component = 'a', ...props }: WrappedProps<P>) {
  return (
    <Component {...props} />
  );
}

С этим вы можете сделать:

interface LinkProps {
  to: string,
  children: ReactNode,
}
function Link({ to, children }: LinkProps) {
  return (
    <a href={to}>{children}</a>
  );
}

function App() {
  return (
    <div>
      <Wrapped<LinkProps> as={Link} to="/foo">Something</Wrapped>
      <Wrapped as="div" style={{ margin: 10 }} />
      <Wrapped />
    </div>
  );
}
0 голосов
/ 11 апреля 2020

Это может работать для вас:

type Props = {
  as?: React.FC<{ to: string, children: any }>,
  to: string,
  children: any
}

const Wrapped: React.FC<Props> = ({ as: El, to, children }) => (
  El
    ? <El to={to}>{children}</El>
    : <a href={to}>{children}</a>
)

Тогда вы можете использовать с as проп:

<Wrapped as={Link} to="/somewhere">label</Wrapped>

... или без значения по умолчанию <a> элемент:

<Wrapped to="/somewhere">label</Wrapped>


В дополнение к вашему комментарию ниже, если вы хотите, чтобы Wrapped принимал реквизиты компонента, переданного в as реквизит, тогда я не уверен, как это сделать или возможно ли это.

Тем не менее, вы можете попробовать что-то вроде этого:

<Wrapped as={<Link to="somewhere">{label}</Link>} />

... так что передача Link в as реквизит таким образом означает, что вам не нужно передавать реквизит Wrapped.

type Props = {
  as?: React.ReactNode
  children: any
}

const Wrapped: React.FC<Props> = ({ as, children }) => (
  <>{ as ? as : <a href="/somewhere">{children}</a>}</>
)
0 голосов
/ 11 апреля 2020

Я думаю, что это может go примерно так:

import React from 'React';

type WrappedProps = {
  as: React.ReactNode,
  to: string;
}

Ссылка: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet

...