Hopp til hovedinnhold

Komposisjon

31. januar 2024

Nokre gonger må du kanskje byte ut ein komponent med ein anna, for eksempel Button skulle vore ein Link. Det er her asChild kjem inn i biletet.

import { Button, Link } from '@digdir/designsystemet-react';
<Button asChild>
<Link href='https://www.digdir.no'>Lenke til digdir.no</Link>
</Button>

I kodesnutten over vert Button-komponenten rendra som ein Link-komponent. Når dette skal ut i DOMen, er det kun eit element som vert rendra. Dette skjer ved hjelp av Radix sin Slot-komponent. 1

Slot mergar sine props ned på komponenten som ligg som underordna element. I tilfellet over vert Button sine props lagt til på Link-komponenten, og ein a-tagg vert rendra ut. Når du brukar asChild kan du ikkje ha meir enn eit underordna element, men du kan ha så mange du vil inne i det elementet.

/* Dette kaster ein error */
<Button asChild>
<Icon />
<Link href='https://www.digdir.no'>Din lenke</Link>
</Button>
/* Dette går fint */
<Button asChild>
<Link href='https://www.digdir.no'>
<Icon />
Din lenke
</Link>
</Button>

Kvifor bruke asChild?

Vi har tidlegare brukt ein as prop for å rendre som andre element. Men når du brukar denne, får du ikkje typesafety eller korrekte typar iht. elementet du har endret til med as. Slot ordnar dette ved at du legg til alle props på det underordna elementet av komponenten, og dermed får typesafety.

<Button asChild>
<Link href='https://www.digdir.no' onClick={() => {}}>
<Icon />
Di lenke
</Link>
</Button>

Alt av klassenavn, aria-attributt og andre props som Button har, vil bli lagt til på Link-komponenten. Dette betyr at vi kan tilby god tilgjengelegheit, samtidig som du kan bruke andre komponentar som du ynskjer.

Event handlers

Dersom ein prop starter med on (td. onClick), så vert det sett på som ein event handler. Når Slot mergar props, vil det lage ein ny funksjon som kallar alle event handlers definert på Button og Link. Funksjonen som ligger på Link vil bli kalla først. Dette betyr at dersom du stoppar eit event på din komponent, vil eventet på Slot-komponenten ikkje bli kalla.

Dersom ein av event handlerane er avhengig av event.defaultPrevented, så må du passe på at rekkefølgja er rett. 2

<Button
asChild
onClick={(event) => {
if (!event.defaultPrevented)
console.log('Ikkje logget til konsoll fordi default er prevented.');
}}
>
<Link onClick={(e) => e.preventDefault()}>Di lenke</Link>
</Button>

Bruk dine eigne komponentar

Fleire komponentar i Designsystemet støttar asChild med standard element. Om du endrar dette, må du passe på at tilgjengelegheit vert teke vare på. Det er sjeldent at du treng å endre det underliggjande elementet, men det er meir realistisk at du vil bruke din eigen komponent.

Ynskjer du å bruke ein eigen komponent må du passe på å spre alle props, og ha støtte for ref. 3

Komponentane dine vil då sjå slik ut:

// uten props og ref
const MinKnapp = () => <button />
// med props
const MinKnapp = (props) => <button {...props} />
// med props og ref
const MinKnapp = React.forwardRef((props, forwardedRef) => (
<button
{...props}
ref={forwardedRef}
/>
))

Fleire Designsystem komponenter støtter asChild.

Referanser

Rediger denne siden på Github (åpnes i ny fane)