Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions apps/docs/src/content/components/list/list/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ description:
zugeordnet.
- Gib jedem Item über `textValue` seinen Text mit. Erst damit findet der User
ein Item über die Tastatur, indem er dessen Anfang tippt.
- Gib jedem `<List.Item />` `renderDependencies` mit und lege Modals und
Datenabfragen auf Listenebene an. So bleibt die List auch mit vielen Einträgen
schnell, siehe [Performance](#performance).

---

Expand Down Expand Up @@ -411,6 +414,92 @@ du fest, ob die Summary oberhalb oder unterhalb der List erscheint.

---

# Performance

Jedes sichtbare Item rendert seinen Inhalt mit allen Komponenten und Hooks, die
darin stecken – beim ersten Laden, bei jedem nachgeladenen Batch und bei jedem
Re-Render. Was ein Item kostet, zahlt die List also einmal pro Item. Ab etwa 100
Items mit Kontextmenü, Badges und Modals wird das spürbar.

## Items mit renderDependencies

Eine inline geschriebene Render-Funktion rendert bei jedem Render der umgebenden
Komponente alle Items neu. Gib `<List.Item />` und `<List.TableRow />`
`renderDependencies` mit: Ein Item rendert dann nur noch neu, wenn sich seine
Daten oder einer der aufgelisteten Werte ändern. Das spart beim Re-Render rund
40 % der Zeit. Liest die Render-Funktion nur die Daten des Items, genügt ein
leeres Array:

```tsx
<DomainList.Item
textValue={(domain) => domain.hostname}
renderDependencies={[]}
>
{(domain) => <DomainListItem domain={domain} />}
</DomainList.Item>
```

- Liste jeden Wert auf, den die Render-Funktion von außerhalb liest. Fehlt
einer, zeigt das Item weiter den Wert, mit dem es zuletzt gerendert wurde. Die
Regeln dazu stehen unter
[Item-Inhalte und externer State](#item-inhalte-und-externer-state).
- Halte die aufgelisteten Werte stabil. Ein inline erzeugter Callback oder ein
inline erzeugtes Objekt ist bei jedem Render neu und rendert damit jedes Item
neu; erzeuge sie mit `useCallback` oder `useMemo`.
- Achte auf stabile Daten. Ein `data.map(…)` im Render erzeugt bei jedem Render
neue Objekte und damit neue Item-Daten; berechne es mit `useMemo`.

## Modals und Kontextmenüs

Ein geschlossenes Modal rendert seinen Inhalt erst beim Öffnen und kostet wenig.
Was sich mit jedem Item vervielfacht, sind der Controller und alles, was die App
um ein Modal legt. Hat ein Item mehrere Modals, öffne ein gemeinsames Modal auf
Listenebene und übergib ihm das gewählte Item:

```tsx
const editModal = useModalController();
const [selected, setSelected] = useState<Domain>();

const edit = useCallback(
(domain: Domain) => {
setSelected(domain);
editModal.open();
},
[editModal],
);

<DomainList.Item
textValue={(domain) => domain.hostname}
renderDependencies={[edit]}
>
{(domain) => <DomainListItem domain={domain} onEdit={edit} />}
</DomainList.Item>;

<Modal controller={editModal}>
{selected && <EditDomainForm domain={selected} />}
</Modal>;
```

- Öffne das Modal aus dem Kontextmenü des Items:
`<MenuItem onAction={() => onEdit(domain)}>`.
- Setze die Auswahl beim Schließen nicht zurück. Sonst verschwindet der Inhalt,
während das Modal noch ausblendet.
- Kombiniere das gemeinsame Modal mit `renderDependencies`. `setSelected`
rendert die umgebende Komponente neu, und ohne `renderDependencies` damit
jedes Item. `edit` ist über `useCallback` stabil und löst kein Neurendern aus.

## Daten und Batches

- Lade Daten auf Listenebene statt pro Item. Ein Request oder eine Subscription
in jedem Item vervielfacht sich mit jedem nachgeladenen Batch.
- Behalte die Standard-Batchgröße von 20 bei. „Mehr anzeigen“ und Infinite
Scroll behalten alle geladenen Items im DOM, und Sortieren, Filtern und Suchen
rendern alle sichtbaren Items neu.
- Registriere globale Listener (etwa für `resize` oder Media Queries) einmal für
die List, nicht in jedem Item.

---

# Properties

<PropertiesTables />
Expand Down
5 changes: 5 additions & 0 deletions packages/components/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ Also:
- Do not hand-roll feedback for async work — `Action` wraps a button and renders
its pending, success and failure states; `List` covers loading and empty
states; `Skeleton` and `SkeletonText` are the placeholders.
- Keep `List` items cheap: give `List.Item` and `List.TableRow`
`renderDependencies` (every value the render function reads besides the item),
and keep modals and data loading at list level instead of per item — every
item pays for what it contains (`/raw/components/list/list.md`, section
"Performance").
- Write UI text in German following the Sprach-Guide; component-internal text is
already translated (de-DE, en-US) via `TranslationProvider`.

Expand Down
Loading