mirror of
https://github.com/vercel/components.build.git
synced 2026-09-14 20:06:39 +08:00
55fae375a4
* Update Geistdocs * Update geistdocs.tsx * Run translation script * Delete toc.tsx * Update route.ts * Update route.ts
233 lines
7.3 KiB
Plaintext
233 lines
7.3 KiB
Plaintext
---
|
|
title: Koostettavuus
|
|
description: Modernien käyttöliittymäkomponenttien rakentamisen perusta.
|
|
---
|
|
|
|
Koostettavuus, tai composability, on modernien käyttöliittymäkomponenttien rakentamisen perusta. Se on yksi tehokkaimmista tekniikoista joustavien, uudelleenkäytettävien komponenttien luomiseksi, jotka pystyvät käsittelemään monimutkaisia vaatimuksia ilman, että API:n selkeys kärsii.
|
|
|
|
Sen sijaan, että kaikki toiminnallisuus ahtauttaisiin yhteen komponenttiin kymmenien propien kanssa, koostettavuus jakaa vastuun useiden yhteistyössä toimivien komponenttien kesken.
|
|
|
|
Fernando piti tästä erinomaisen esityksen React Universe Conf 2025 -tapahtumassa, jossa hän jakoi lähestymistapansa Slackin Message Composerin rakentamiseen uudelleen koostettavana komponenttina.
|
|
|
|
<Video src="https://www.youtube.com/watch?v=4KvbVq3Eg5w" />
|
|
|
|
## Komponentin tekeminen koostettavaksi
|
|
|
|
Jotta komponentista tulisi koostettava, se täytyy pilkkoa pienempiin, tarkemmin fokusoituihin komponentteihin. Otetaan esimerkiksi tämä Accordion-komponentti:
|
|
|
|
```tsx title="accordion.tsx"
|
|
import { Accordion } from '@/components/ui/accordion';
|
|
|
|
const data = [
|
|
{
|
|
title: 'Accordion 1',
|
|
content: 'Accordion 1 content',
|
|
},
|
|
{
|
|
title: 'Accordion 2',
|
|
content: 'Accordion 2 content',
|
|
},
|
|
{
|
|
title: 'Accordion 3',
|
|
content: 'Accordion 3 content',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Accordion data={data} />
|
|
);
|
|
```
|
|
|
|
Vaikka tämä Accordion-komponentti saattaa vaikuttaa yksinkertaiselta, se käsittelee liian monia vastuita. Se on vastuussa säiliön, triggerin ja sisällön renderöinnistä; sekä accordion-tilan ja datan käsittelystä.
|
|
|
|
Tämän komponentin tyylien mukauttaminen on vaikeaa, koska se on tiukasti kytketty. Se todennäköisesti vaatii globaaleja CSS-overrideja. Lisäksi uuden toiminnallisuuden lisääminen tai käyttäytymisen hienosäätö vaatii komponentin lähdekoodin muokkaamista.
|
|
|
|
Ratkaisuna voimme jakaa tämän pienempiin, tarkemmin fokusoituihin komponentteihin.
|
|
|
|
### 1. Root-komponentti
|
|
|
|
Aloitetaan säiliöstä — komponentista, joka pitää kaiken koossa, eli triggeristä ja sisällöstä. Tämän säiliön ei tarvitse tietää datasta, mutta sen täytyy pitää kirjaa avoimesta tilasta.
|
|
|
|
Haluamme kuitenkin, että tämä tila on lapsikomponenttien saatavilla. Käytetään siis Context API:a luodaksemme kontekstin avoimelle tilalle.
|
|
|
|
Lopuksi, jotta `div`-elementtiä voidaan muokata, laajennamme oletusarvoisia HTML-attribuutteja.
|
|
|
|
Kutsumme tätä komponenttia "Root"-komponentiksi.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
type AccordionProps = React.ComponentProps<'div'> & {
|
|
open: boolean;
|
|
setOpen: (open: boolean) => void;
|
|
};
|
|
|
|
const AccordionContext = createContext<AccordionProps>({
|
|
open: false,
|
|
setOpen: () => {},
|
|
});
|
|
|
|
export type AccordionRootProps = React.ComponentProps<'div'> & {
|
|
open: boolean;
|
|
setOpen: (open: boolean) => void;
|
|
};
|
|
|
|
export const Root = ({
|
|
children,
|
|
open,
|
|
setOpen,
|
|
...props
|
|
}: AccordionRootProps) => (
|
|
<AccordionContext.Provider value={{ open, setOpen }}>
|
|
<div {...props}>{children}</div>
|
|
</AccordionContext.Provider>
|
|
);
|
|
```
|
|
|
|
### 2. Item-komponentti
|
|
|
|
Item-komponentti on elementti, joka sisältää yksittäisen accordion-kohteen. Se on yksinkertaisesti wrapper kullekin kohteelle accordionissa.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionItemProps = React.ComponentProps<'div'>;
|
|
|
|
export const Item = (props: AccordionItemProps) => <div {...props} />;
|
|
```
|
|
|
|
### 3. Trigger-komponentti
|
|
|
|
Trigger-komponentti on elementti, joka avaa accordionin, kun sitä aktivoidaan. Se on vastuussa:
|
|
|
|
- Renderöinnistä oletuksena napiksi (voidaan mukauttaa `asChild`-propin avulla)
|
|
- Klikkitapahtumien käsittelystä accordionin avaamiseksi
|
|
- Fokuksen hallinnasta, kun accordion sulkeutuu
|
|
- Oikeiden ARIA-attribuuttien tarjoamisesta
|
|
|
|
Lisätään tämä komponentti Accordion-komponenttiimme.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionTriggerProps = React.ComponentProps<'button'> & {
|
|
asChild?: boolean;
|
|
};
|
|
|
|
export const Trigger = ({ asChild, ...props }: AccordionTriggerProps) => (
|
|
<AccordionContext.Consumer>
|
|
{({ open, setOpen }) => (
|
|
<button onClick={() => setOpen(!open)} {...props} />
|
|
)}
|
|
</AccordionContext.Consumer>
|
|
);
|
|
```
|
|
|
|
### 4. Content-komponentti
|
|
|
|
Content-komponentti on elementti, joka sisältää accordionin sisällön. Se on vastuussa:
|
|
|
|
- Sisällön renderöimisestä, kun accordion on avoinna
|
|
- Oikeiden ARIA-attribuuttien tarjoamisesta
|
|
|
|
Lisätään tämä komponentti Accordion-komponenttiimme.
|
|
|
|
```tsx title="@/components/ui/accordion.tsx"
|
|
export type AccordionContentProps = React.ComponentProps<'div'> & {
|
|
asChild?: boolean;
|
|
};
|
|
|
|
export const Content = ({ asChild, ...props }: AccordionContentProps) => (
|
|
<AccordionContext.Consumer>
|
|
{({ open }) => <div {...props} />}
|
|
</AccordionContext.Consumer>
|
|
);
|
|
```
|
|
|
|
### 5. Yhdistäminen
|
|
|
|
Nyt kun meillä on kaikki komponentit, voimme yhdistää ne alkuperäiseen tiedostoomme.
|
|
|
|
```tsx title="accordion.tsx"
|
|
import * as Accordion from '@/components/ui/accordion';
|
|
|
|
const data = [
|
|
{
|
|
title: 'Accordion 1',
|
|
content: 'Accordion 1 content',
|
|
},
|
|
{
|
|
title: 'Accordion 2',
|
|
content: 'Accordion 2 content',
|
|
},
|
|
{
|
|
title: 'Accordion 3',
|
|
content: 'Accordion 3 content',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Accordion.Root open={false} setOpen={() => {}}>
|
|
{data.map((item) => (
|
|
<Accordion.Item key={item.title}>
|
|
<Accordion.Trigger>{item.title}</Accordion.Trigger>
|
|
<Accordion.Content>{item.content}</Accordion.Content>
|
|
</Accordion.Item>
|
|
))}
|
|
</Accordion.Root>
|
|
);
|
|
```
|
|
|
|
## Nimeämiskäytännöt
|
|
|
|
Kun rakennat koostettavia komponentteja, yhdenmukaiset nimeämiskäytännöt ovat ratkaisevia intuitiivisten ja ennustettavien API:en luomiseksi. Sekä shadcn/ui että Radix UI noudattavat vakiintuneita malleja, jotka ovat muodostuneet de facto -standardiksi React-ekosysteemissä.
|
|
|
|
### Root-komponentit
|
|
|
|
`Root`-komponentti toimii pääsäiliönä, joka käärii kaikki muut alikomponentit. Se hallinnoi tyypillisesti jaettua tilaa ja kontekstia tarjoamalla kontekstin kaikille lapsikomponenteille.
|
|
|
|
```tsx
|
|
<AccordionRoot>{/* Child components */}</AccordionRoot>
|
|
```
|
|
|
|
### Interaktiiviset elementit
|
|
|
|
Interaktiiviset komponentit, jotka laukaisevat toimintoja tai vaihtavat tiloja, käyttävät kuvaavia nimiä:
|
|
|
|
- `Trigger` - Elementti, joka aloittaa toiminnon (avaaminen, sulkeminen, kytkeminen)
|
|
- `Content` - Elementti, joka sisältää pääasiallisen sisällön, joka näytetään/piilotetaan
|
|
|
|
```tsx
|
|
<CollapsibleTrigger>Click to expand</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
Hidden content revealed here
|
|
</CollapsibleContent>
|
|
```
|
|
|
|
### Sisällön rakenne
|
|
|
|
Komponenteille, joilla on jäsenneltyjä sisältöalueita, käytä semanttisia nimiä, jotka kuvaavat niiden tarkoitusta:
|
|
|
|
- `Header` - Yläsivu, joka sisältää otsikot tai ohjaimet
|
|
- `Body` - Pääsisältöalue
|
|
- `Footer` - Alatunniste alue toimille tai metadataan
|
|
|
|
```tsx
|
|
<DialogHeader>
|
|
{/* Dialog title */}
|
|
</DialogHeader>
|
|
<DialogBody>
|
|
{/* Dialog content */}
|
|
</DialogBody>
|
|
<DialogFooter>
|
|
{/* Dialog footer */}
|
|
</DialogFooter>
|
|
```
|
|
|
|
### Informatiiviset komponentit
|
|
|
|
Komponentit, jotka tarjoavat tietoa tai kontekstia, käyttävät kuvaavia päätteitä:
|
|
|
|
- `Title` - Ensisijainen otsikko tai etiketti
|
|
- `Description` - Tukiteksti tai selittävä sisältö
|
|
|
|
```tsx
|
|
<CardTitle>Project Statistics</CardTitle>
|
|
<CardDescription>
|
|
View your project's performance over time
|
|
</CardDescription>
|
|
``` |