# Documenten en nodes

> Een document is een boom van componenten, die elk hun data als state aannemen en de nodes teruggeven die ze willen.

Source: https://docxcelerate.com/nl/docs/essentials/documents-and-nodes/

Een document is een template plus een datatype. Het template is een boom van
**componenten**; elk geeft **nodes** terug, en de boom oplossen tegen data levert
een `DocumentModel` op — pure JSON, zonder dat daarmee iets over rendering gezegd
is.

## Een component neemt zijn data als state

```tsx
import { Paragraph, useState } from "docxcelerate/template";
import type { DocumentData } from "../types.ts";

export const Greeting: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    name: data.residentName,
  }));

  return <Paragraph id="greeting">Dear {state.name},</Paragraph>;
};
```

`useState` is waar data een component binnenkomt, en de enige plek. Alles daarna
leest state. Dat is een regel met een reden: doordat elke afhankelijkheid door
één declaratie loopt, staat opgeschreven wat een component nodig heeft in plaats
van verspreid door de code die het gebruikt.

De initializer is gewoon TypeScript. Er is geen templatetaal, dus condities,
formattering en imports zijn simpelweg beschikbaar.

`Paragraph` benoemt zowel het element als het componenttype — een waarde en een
type mogen een naam delen — dus `const Greeting: Paragraph` zegt wat dit
oplevert, en er een `<Section>` uit teruggeven is een compileerfout.

## Data kan ook als props binnenkomen

Een component die krijgt wat hij nodig heeft, hoeft er niet zelf naar te grijpen:

```tsx
export const Arrears: Paragraph<{ amount: number }> = ({ amount }) => {
  const { currency } = useFormat();

  return <Paragraph id="arrears">You owe {currency(amount)}.</Paragraph>;
};
```

Iemand moet de data eerst lezen, dus een component met alleen props zit onder een
ouder die ze als state heeft aangenomen. Gebruik props voor componenten die je
tegen verschillende waarden wilt hergebruiken, en state voor componenten die
weten bij welk document ze horen.

## Beslissingen zijn een gewone `if`

```tsx
export const Balance: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    settled: data.balanceDue === 0,
  }));

  if (state.settled) {
    return <Paragraph id="settled">Nothing outstanding.</Paragraph>;
  }

  return <Paragraph id="arrears">A balance remains.</Paragraph>;
};
```

Geef elke tak zijn eigen id. Het opgeloste document legt dan vast welke deze
ontvanger kreeg, en dat is het verschil tussen een document dat je kunt
controleren en een dat je alleen kunt bekijken.

Er is één ding om te weten over vertakken voordat je naar een engine publiceert:
zie [wat er verandert bij publiceren](/nl/docs/essentials/templates/#wat-er-verandert-bij-publiceren).

## Ids

Een id is hoe een engine een node adresseert en hoe buildartefacten tussen runs
diffbaar blijven, dus behandel een hernoeming als een breaking change.

Je mag hem weglaten. Een node zonder id krijgt er een van waar hij staat, en dat
voorkomt dat takken en lussen je dwingen namen te verzinnen. Wat niet kan, is er
een twee keer gebruiken: twee nodes die één id claimen is een fout, gemeld met
beide posities, en geen race die de laatste wint.

## Secties nesten, nodes niet

`Section` is de enige constructie die nest. De titel gaat mee in de
documentstructuur:

```tsx
<Section id="opening" title="Opening">
  <Greeting />
  <Offer />
</Section>
```

Zie [Section](/nl/docs/nodes/section/) voor wat een sectie mag bevatten en hoe
diep.

## Hooks

Hooks zijn hoe een component de build om zich heen bereikt:

| Hook | Wat het je geeft |
| --- | --- |
| `useState` | Data, één keer aangenomen en bewaard |
| `useShared` | Een waarde achtergelaten voor de componenten die daarna renderen |
| `useSetPrompts` | Prompts voor de node die dit oplevert — wat hem dynamisch maakt |
| `useSetPlaceholders` | Wat previews tonen in plaats van gegenereerde inhoud |
| `usePlaceholderData` | Vervangende namen, datums en cijfers voor previews |
| `useFormat` | Landinstellingbewuste valuta, datums, lijsten en meervouden |
| `useAvailableTokens` | Het tokenbudget dat deze build heeft toegewezen |
| `useDeriver` | Voert nu een geregistreerde deriver uit |

Ze lopen in aanroepvolgorde, dus elke hook moet bereikt zijn vóór de eerste
`await` en vóór elke vertakking — dezelfde regel die React hanteert, om dezelfde
reden. Er een aanroepen na een `await` is een fout die dat ook zegt, in plaats
van zich stilletjes vast te haken aan de volgende component die rendert.

## Het document bouwen

```ts
import { buildDocument } from "docxcelerate";

const doc = await buildDocument(documentTemplate, data);
```

Het resultaat is een `DocumentModel`: een `schemaVersion`, een `id`, een `title`
en een `nodes`-array. Het bevat geen styling en geen lay-out — die worden later
toegepast door de renderer die het verwerkt.

Voor een documentproject gedefinieerd met `defineDocumentProject` gebruik je
liever `buildProjectPreviewDocument`, dat de stijl van het project toepast en
dynamische nodes naar hun placeholders oplost:

```ts
import { buildProjectPreviewDocument } from "docxcelerate";

const doc = await buildProjectPreviewDocument(project);
```

Dat is precies wat de preview-app aanroept, dus wat je in code bouwt komt overeen
met wat je in de browser zag.
