Creazione di componenti polimorfici con React e TypeScript

Jan 05 2023
L'aggiunta di polimorfismo ai tuoi componenti React li renderà più versatili in quanto i consumatori saranno in grado di definire l'elemento reso al DOM. Ci sono passaggi importanti da seguire durante la creazione di questi componenti, quindi iniziamo! Perché abbiamo bisogno di componenti polimorfici? Potresti aver notato in alcune librerie come (Material UI o Chakra UI) alcuni componenti hanno la possibilità di consentire un as o elementType prop.

L'aggiunta di polimorfismo ai tuoi componenti React li renderà più versatili in quanto i consumatori saranno in grado di definire l'elemento reso al file DOM.

Ci sono passaggi importanti da seguire durante la creazione di questi componenti, quindi iniziamo!

Perché abbiamo bisogno di componenti polimorfici?

Potresti aver notato in alcune librerie come ( Material UI o Chakra UI ) alcuni componenti hanno la possibilità di consentire un aso elementTypeprop. Questo è importante per la personalizzazione ma anche per migliorare l'accessibilità permettendoci di utilizzare elementi semantici HTML durante il rendering dei nostri componenti.

Aggiunta di un asoggetto di scena a un componente React

Vediamo come potremmo implementare qualcosa di simile a queste librerie: inizieremo con un semplice Textcomponente React.

const Text = ({ as, children, ...rest }) => {
  const Component = as || "span";

  return <Component {...rest}>{children}</Component>;
};

export default function App() {
  return <Text>Text goes here</Text>; // Will render a span by default
}

<Text as="main">Text goes here</Text>;
<Text as="p">Text goes here</Text>;
<Text as="section">Text goes here</Text>;
<Text as="a" href="www.example.com">Text goes here</Text>

Funziona come un fascino! Oh, ma aspetta...

<Text as="p" href="www.example.com">
    Text goes here
</Text>

      
                

Ecco che arriva TypeScript

È qui che entra in gioco il nostro migliore amico TypeScript.

Dobbiamo assicurarci che gli Textoggetti di scena dei componenti siano fortemente tipizzati in modo che possano essere controllati staticamente durante il processo di sviluppo.

Il piano è consentire al nostro asprop di accettare qualsiasi elemento HTML valido e quindi determinare in qualche modo quali attributi HTML sono validi in base a quello stesso elemento (ad esempio un atag dovrebbe consentire l' hrefattributo).

Creare i nostri tipi

Iniziamo con l'impostazione di un tipo per l' aselica.

import { ElementType } from "react";

type AsProp<T extends ElementType> = {
  /**
   * The HTML element used for the root node.
   */
  as?: T;
};

Il nostro tipo Tè limitato ai tipi che si estendono ElementTypeda React, ovvero qualsiasi elemento HTML valido.

import { ComponentPropsWithoutRef } from "react";

ComponentPropsWithoutRef<T>

Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>

Props<T> & Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>

I nostri tipi combinati dovrebbero assomigliare a questo:

type TextProps<T extends ElementType> = PropsWithChildren<AsProp<T>> &
  Omit<ComponentPropsWithoutRef<T>, keyof AsProp<T>>;

Finalmente TextPropsha tutto ciò di cui abbiamo bisogno Text! Mettiamo tutto insieme e aggiungiamo il tipo al nostro Textcomponente:

import {
  ElementType,
  ComponentPropsWithoutRef,
  PropsWithChildren
} from "react";

type AsProp<T extends ElementType> = {
  /**
   * The HTML element used for the root node.
   */
  as?: T;
};

type TextProps<T extends ElementType> = PropsWithChildren<AsProp<T>> &
  Omit<ComponentPropsWithoutRef<T>, keyof AsProp<T>>;

const Text = <T extends ElementType = "span">({
  as,
  children,
  ...rest
}: TextProps<T>) => {
  const Component = as || "span";

  return <Component {...rest}>{children}</Component>;
};

La cosa migliore della nostra configurazione è che ora ...restè anche fortemente tipizzato.

Quindi questo dovrebbe funzionare:

<Text as="a" href="www.google.com">
   Text goes here
</Text>

<Text as="main" href="www.google.com">
   Text goes here
</Text>