Búsqueda de sitios web

reactjs - ¿Cómo escribir en TypeScript un componente de React que acepte otro componente como prop, y todas las props de ese componente?


Tengo un componente de React <Wrapper> que puedo usar de la siguiente manera:

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

Si no se pasa ninguna propiedad as, asumirá un <a>. Pero si se pasa un componente a as, todas las propiedades que son válidas para ese componente ahora también deberían ser propiedades válidas de Wrapped.

¿Hay una manera de escribir esto en TypeScript? Actualmente estaba pensando en algo como esto:

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

Sin embargo, no estoy seguro de si JSX.Element y/o React.ComponentProps son los tipos relevantes aquí, y esto no compila porque El no se puede pasar a ComponentProps. ¿Cuáles serían los tipos correctos allí, y algo como esto es siquiera posible?

Respuesta #1

Los tipos que necesitas son ComponentType y 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} />
  );
}

Con eso, puedes hacer:

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>
  );
}

Artículos relacionados