# Funktionsweise der Erweiterungen der Nutzeroberfläche

Lernen Sie das Erweiterungssystem der Nutzeroberfläche kennen und erfahren Sie, wie Sie das Stripe-Dashboard erweitern können.

Mit Erweiterungen der Nutzeroberfläche von Stripe-Apps können Sie Ihre eigene Nutzeroberfläche mit [TypeScript](https://www.typescriptlang.org/) und [React](https://reactjs.org/) in den Produkten von Stripe rendern. Diese Tools sollten Ihnen vertraut sein, wenn Sie Produkte in React entwickelt haben. Da sie jedoch in einem [sicheren Sandbox-iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) ausgeführt werden, der in eine andere Webseite eingebettet ist, unterscheiden sie sich in mehrfacher Hinsicht von standardmäßigen browserbasierten React-Anwendungen.

## Übersicht

- [Einführung in React](https://reactjs.org/tutorial/tutorial.html)
- [Erste Schritte mit TypeScript](https://www.typescriptlang.org/docs/)
- [Bestandteile der Stripe-Nutzeroberfläche](https://docs.stripe.com/stripe-apps/components.md)

Erweiterungen der Nutzeroberfläche werden in TypeScript erstellt und nutzen React zur Erstellung einer Nutzeroberfläche mit dem [UI-Toolkit von Stripe](https://docs.stripe.com/stripe-apps/components.md). Anders als bei anderen React-Umgebungen unterstützen Erweiterungen der Nutzeroberfläche kein beliebiges HTML. Stattdessen verwenden Sie ausschließlich die von Stripe bereitgestellten UI-Komponenten. Die Struktur von Nutzeroberflächenerweiterungen umfasst einige wichtige Verzeichnisse und Dateien:

- `stripe-app.json`: Das *App-Manifest* (In a Stripe App, the app manifest is a stripe-app.json file in your app's root directory. It defines your app's ID, views, permissions, and other essential properties). Es beschreibt, wie Apps mit Stripe interagieren, einschließlich der erforderlichen Berechtigungen, ob eine Erweiterung der Nutzeroberfläche existiert und, falls dies der Fall ist, wo diese Erweiterung in der Nutzeroberfläche von Stripe angezeigt wird.
- `package.json`: NPM-Paket-Metadaten. Die Erweiterungen der Nutzeroberfläche sind gewöhnliche [NPM-Pakete](https://docs.npmjs.com/about-packages-and-modules). Abhängigkeiten können mit [npm](https://docs.npmjs.com/cli) oder [yarn](https://yarnpkg.com/) verwaltet werden.
- `src`: Der eigentliche TypeScript-Quellcode für die Erweiterung der Nutzeroberfläche. Standardmäßig platziert die CLI eine allgemeine Ansicht in `src/views` mit einem entsprechenden Eintrag in `stripe-app.json`.

Die Entwicklung einer Erweiterung der Nutzeroberfläche stützt sich auf das Stripe-CLI-App-Plugin. Die CLI kümmert sich um die Initialisierung von Apps mit der korrekten Struktur, die Konfiguration des App-Manifests, die Ausführung des Entwicklungsservers und die entsprechende Bündelung der App für die Übermittlung an Stripe.

### Entwicklung einer Erweiterung der Nutzeroberfläche

- Als App-Entwickler erstellen Sie Ansichten, d.&nbsp;h. React-Komponenten, die immer dann angezeigt werden, wenn ein bestimmtes [Darstellungsfeld](https://docs.stripe.com/stripe-apps/reference/viewports.md) auf dem Bildschirm angezeigt wird. Damit eine Ansicht beispielsweise immer dann angezeigt wird, wenn eine Seite mit Rechnungsdetails betrachtet wird, registrieren Sie sie für das Darstellungsfeld `stripe.dashboard.invoice.detail`.
- Wenn Sie bereit sind, Ihre App hochzuladen, helfen CLI-Befehle Ihnen, Ihren Code zu bündeln, ihn bei Stripe hochzuladen und Ihre App auf dem CDN von Stripe zu hosten.
- Wenn die Erweiterung der Nutzeroberfläche Ihrer App initialisiert wird, lädt Stripe den Code der App in einen Sandbox-iFrame herunter.
- Wenn ein/e Nutzer/in eine Seite aufruft, die ein bestimmtes Darstellungsfeld enthält (zum Beispiel `/invoices/inv_1283`):
  - Stripe definiert die Ansicht der Erweiterung der Nutzeroberfläche innerhalb des iFrame mit dem vom Darstellungsfeld bereitgestellten Kontext.
  - Stripe leitet die Ansicht an das Dashboard weiter, damit sie den Nutzer/innen angezeigt werden kann.
  - Wenn Nutzer/innen mit der Erweiterung der Nutzeroberfläche interagieren (zum Beispiel durch Klicken auf eine Schaltfläche), empfangen Ereignis-Handler im iFrame der Erweiterung das Ereignis und können die Ansicht aktualisieren.
![Systemdiagramm der Nutzeroberflächenerweiterung der Stripe-App](https://b.stripecdn.com/docs-statics-srv/assets/extensions-structure.d6d4d0512ef2194eda5c7ffd214e894e.jpg)

## Ansichten und Darstellungsfelder

Um den Nutzer/innen die Nutzeroberfläche einer App anzuzeigen, erstellen Sie eine React-Ansicht registrieren diese bei einem Darstellungsfeld.

Ansichten sind React-Komponenten, die die App exportiert. Darstellungsfelder sind Bezeichner, die angeben, wo die Ansicht angezeigt werden soll. Wenn Sie eine App hochladen, werden alle von der App registrierten Ansichten mit dem zugehörigen Darstellungsfeld registriert.

Um Ihrer App eine eigene Dashboard-Seite zu geben, registrieren Sie eine Ansicht im [`stripe.dashboard.fullpage`-Viewport](https://docs.stripe.com/stripe-apps/reference/viewports.md). Das Dashboard rendert die Ansicht als Seite, die Nutzer/innen über die linke Navigation öffnen können. Innerhalb der Seite ordnet [routing](https://docs.stripe.com/stripe-apps/routing.md) URL-Pfade Bildschirmen zu, die Nutzer/innen als Lesezeichen speichern und teilen können. Einrichtungs- und Layoutmuster finden Sie unter [Ganzseitige Apps](https://docs.stripe.com/stripe-apps/patterns/full-page-apps.md).

Ansichten werden automatisch in Darstellungsfeldern erfasst, wenn `stripe apps add view` ausgeführt wird. Im Hintergrund wird dabei ein Eintrag zum *App-Manifest* (In a Stripe App, the app manifest is a stripe-app.json file in your app's root directory. It defines your app's ID, views, permissions, and other essential properties) hinzugefügt.

```json
{
  //... other manifest properties
  "ui_extension": {
    "views": [
      {
          "viewport": "stripe.dashboard.invoice.detail", // See all valid values at stripe.com/docs/stripe-apps/reference/viewports
          "component": "NameOfComponent" // This is provided by you
      }
      // ... additional views
    ]
  }
}
```

## Lebenszyklus der Erweiterung einer Nutzeroberfläche

Erweiterungen der Nutzeroberfläche werden in einem unsichtbaren Sandbox-iFrame ausgeführt, der asynchron Aktualisierungen der Nutzeroberfläche an das Stripe-Dashboard sendet. Ein einzelner Sandbox-iFrame kann mehrere Ansichten gleichzeitig aufnehmen.

Der Lebenszyklus des Sandbox-iFrames und der von ihm unterstützten Ansichten funktioniert wie folgt:

- Das Dashboard lädt die den iFrame der Erweiterung der Nutzeroberfläche. Dies geschieht zwischen dem Laden des Dashboard und dem Öffnen der App durch die Nutzer/innen.
- Wenn eine Ansicht angezeigt werden muss, wartet das Dashboard auf die Initialisierung des Sandbox-iFrame und weist ihn dann an, die richtige Ansicht bereitzustellen und den [entsprechenden Kontext](https://docs.stripe.com/stripe-apps/reference/extensions-sdk-api.md#props) zu übergeben.
- Wenn Nutzer/innen einen App-Drawer schließen oder eine ganzseitige App verlassen, wird die entsprechende Ansicht entfernt. Das Entfernen der Ansicht entfernt sie aus dem DOM und aus dem Sandbox-React-Tree.
- Der Sandbox-iFrame kann je nach Ressourcenauslastung weiterhin ausgeführt oder abgeschaltet werden. Die einzige Garantie ist, dass das Dashboard so gut wie möglich versucht, [useEffect](https://reactjs.org/docs/hooks-effect.html) und andere Bereinigungshandler ausführen zu lassen, bevor der iFrame beendet wird.
![Lebenszyklus der Erweiterung der Nutzeroberfläche von Stripe Apps](https://b.stripecdn.com/docs-statics-srv/assets/extensions-lifecycle.0fef55cf88daa063de38151596ad17e5.jpg)

Lebenszyklus der Erweiterung der Nutzeroberfläche von Stripe Apps

## Einschränkungen für Sandbox-iFrame

Aufgrund der speziellen Sandbox-Umgebung, in der der Quelltext der Nutzeroberflächenerweiterung ausgeführt wird, können Nutzeroberflächenerweiterungen von Stripe Apps nicht alles, was reguläre React-Apps können, die im Browser ausgeführt werden.

### Hauptunterschiede zwischen Stripe Apps und regulären React-Apps

1. Stripe Apps haben keinen direkten Zugriff auf das DOM. Sie werden in einem iFrame ausgeführt mit einem separaten DOM, das vom Dashboard aus nicht sichtbar ist.
2. Das Dashboard leitet alle Daten an die App weiter und serialisiert diese. Komponenten des UI-Toolkits akzeptieren nur serialisierbare Daten.
3. Das Dashboard leitet auch alle *Props* an die App weiter und serialisiert die App, sodass Funktionen, die an Komponenten des UI-Toolkits übergeben oder von diesen ausgelöst werden, asynchron sind.

### Einschränkungen bei React und JavaScript

Die folgenden Einschränkungen wirken sich darauf aus, was Sie mit React und JavaScript tun können, wenn Sie Ihre App entwickeln. Die React-Baumstruktur wird erst dann in das DOM gerendert, wenn die Hostumgebung vom Stripe-Dashboard deserialisiert und ausgewertet wird. Das DOM für die App wird aktualisiert und die React-Instanz im Dashboard verwaltet dann die Dateneingabe.

#### Globale Dokument- und Fensterobjekte sind eingeschränkt

Die DOM-Umgebung, in der der Erweiterungscode der Nutzeroberfläche ausgeführt wird, ist durch den [Sandbox-iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) gesperrt. Dies bedeutet, dass APIs der obersten Ebene wie [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), [indexedDB](https://developer.mozilla.org/en-US/docs/web/api/indexeddb) und [BroadcastChannel](https://developer.mozilla.org/en-US/docs/web/api/broadcastchannel) nicht verfügbar sind. Jede DOM API, die sich auf die [same-origin-Richtlinie](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) stützt, funktioniert nicht wie erwartet, da Sandbox-iFrames einen `null`-Ursprung haben.

#### Referenzeigenschaften von React werden nicht unterstützt

UI-Komponenten unterstützen keine `ref`-Eigenschaften von React, da die React-Baumstruktur serialisiert und zum Rendern an das Stripe Dashboard übergeben wird. Das DOM, in das die Komponenten schließlich gerendert werden, ist für den App-Code, der im Sandbox-iFrame ausgeführt wird, nicht zugänglich.

#### Apps können die React-Version nicht steuern

Die Standarddatei `package.json`, die von jeder Stripe-App generiert wird, hat keinen `dependency`-Eintrag für `react`. Durch das Hinzufügen einer bestimmten Version von React in der `package.json`-Datei der Stripe-App wird nicht gesteuert, welche React-Version Ihre App rendert. Es werden nur Typprüfungen und Komponententests durchgeführt. Das Stripe-Dashboard verwendet seine eigene Version von React (derzeit Version 17.0.2), um die Apps anzuzeigen. Um stets Kompatibilität zu gewährleisten, ändern Sie die Version nur, wenn Stripe Sie dazu auffordert.

#### Verwendung nicht gesteuerter Komponenten für Interaktionen

Das Dashboard serialisiert alle Dateneingaben und übermittelt sie über einen Proxy an die App. Dies führt zu einer Eingabeverzögerung bei Verwendung von React-[gesteuerten Komponenten](https://reactjs.org/docs/forms.html#controlled-components). Diese Verzögerung ist für die Nutzer/innen wahrnehmbar und kann möglicherweise Zeichen überschreiben, die Nutzer/innen in der Zwischenzeit eingegeben haben. Außerdem kann der Cursor dadurch zum Ende einer Texteingabe springen, wenn sie versuchen, Text am Anfang zu bearbeiten.

Verwenden Sie nicht gesteuerte Benutzereingaben, um die App-Nutzung zu beschleunigen:

```jsx
import {useState} from 'react';
import {TextArea} from '@stripe/ui-extension-sdk/ui';

const App = () => {
  const defaultValue = 'Initial TextArea value';
  const [text, setText] = useState(defaultValue);
  return (
    <>
      {/* This doesn't work */}
      {/* Attempting to edit text at the beginning skips the cursor to the end */}
      <TextArea
        label="Message"
        value={text}
        onChange={e => setText(e.target.value)}
      />
      {/* This will work */}
      <TextArea
        label="Message"
        defaultValue={defaultValue}
        onChange={e => setText(e.target.value)}
      />
    </>
  );
};
```

### UI-Komponenten: Einschränkungen

Die folgenden Einschränkungen gelten für [Komponenten der Nutzeroberfläche](https://docs.stripe.com/stripe-apps/components.md). Während Ihre Erweiterung in einer isolierten Umgebung ausgeführt wird, werden die UI-Komponenten direkt im Dashboard gerendert. Das SDK informiert das Dashboard, um die UI-Komponenten zu rendern, was zu den folgenden Einschränkungen führt.

#### Komponenten können die Ereignisverbreitung nicht stoppen

Da Ereignis-Handler zeitversetzt aufgerufen werden, hat die Ereignisverbreitung bereits stattgefunden, wenn der Ereignis-Handler der App aufgerufen wird. Daher kann die App Ereignisausbreitung bzw. Bubbling nicht stoppen.

#### Komponenten akzeptieren nur serialisierbare Datentypen als Eigenschaften

Komponenten der Nutzeroberfläche akzeptieren nur serialisierbare Datentypen. Wenn die nicht serialisierbaren Datentypen wie `Map` oder `Set` als Eigenschaft an eine Komponente des UI-Toolkits übergeben werden, wird ein Fehler ausgegeben.

Verwenden Sie nur einfache Typen, Funktionen oder React-Ereignisse als Eigenschaften. Folgende Typen werden unterstützt:

- Zeichenfolgen, Zahlen, `true`, `false`, `null` und `undefined`
- Objekte, deren Schlüssel und Werte einfache Typen sind
- Arrays, deren Werte einfache Typen sind
- Funktionen, sie werden jedoch asynchron, wenn sie über einen Proxy übermittelt werden. Alle als Argumente übergebene oder zurückgegebene Funktionen unterliegen auch den Typenbeschränkungen
- React-Ereignisse

> Diese Einschränkung für serialisierbare Datentypen gilt *ausschließlich* für Komponenten, die von `@stripe/ui-extension-sdk/ui` importiert werden (z.&nbsp;B. `Box`, `Img`, `Schaltfläche` usw.). Ihre eigenen benutzerdefinierten Komponenten können jeden Datentyp als Eigenschaften akzeptieren (einschließlich tief verschachtelter Objekte, zirkulärer Referenzen oder komplexer Instanzen), solange Sie diese Werte nicht direkt an die SDK-Komponenten übergeben.

```jsx
import { Img } from "@stripe/ui-extension-sdk/ui";

// Object with circular reference
const circularObj = { name: 'example' };
circularObj.self = circularObj;

// ✅ OK: Passing `circularObj` to your own component
function Card({ src, data }) {
  // ❌ NOT OK: Passing `data` to an SDK component
  return <Img src={src} width={100} height={100} data={data} />;
}

function App() {
  // ✅ OK: Custom component can receive complex objects
  return <Card src="https://example.com/image.jpg" data={circularObj} />;
}
```

#### Komponenten unterstützen keine Rendering-Funktionen

React rendert synchron, aber Funktionen, die an Komponenten der Nutzeroberfläche übergeben werden, werden asynchron, nachdem das Dashboard sie über einen Proxy an die App übermittelt hat. Funktionen, die an eine Komponente der Nutzeroberfläche übergebene Aufschläge generieren, beenden das Rendering nicht rechtzeitig genug, als dass React ihre Ergebnisse verwenden könnte. Infolgedessen akzeptieren Komponenten der Nutzeroberfläche keine Rendering-Funktionen.

Dies bedeutet, dass die folgenden Muster nicht funktionieren:

```jsx
// This doesn't work ❌
<ItemProvider>
  {(data) => (
    <Item data={data} />
  )}
</ItemProvider>
```

```jsx
// This doesn't work ❌
<Item renderFooter={() => <div>footer</div>} />
```

#### JSX kann nur als einfacher Knoten an nicht untergeordnete Eigenschaften übergeben werden

UI-Komponenten unterstützen Eigenschaften, die ein einfaches React-Element verwenden:

```jsx
// This will work ✅
<Item footer={<div>footer</div>} />
```

Komplexere JSX-Datenstrukturen werden jedoch nicht unterstützt:

```jsx
// This doesn't work ❌
<Item footer={[<div>one</div>, <div>two</div>]} />
```

```jsx
// This doesn't work ❌
<Item footer={{
  one: <div>one</div>,
  two: <div>two</div>
}} />
```

Wenn Sie mehrere React-Elemente an eine UI-Komponente übergeben müssen, schließen Sie diese in ein Fragment ein:

```jsx
// This works ✅
<Item footer={
  <>
    <div>one</div>
    <div>two</div>
  </>
}/>
```

Eine ähnliche Beschränkung gilt für `children`. Arrays und Objekte, die JSX enthalten, werden nicht unterstützt. Mehrere React-Elemente sind jedoch zulässig:

```jsx
// This works ✅
<Item>
  <div>one</div>
  <div>two</div>
</Item>
```

## NPM-Pakete installieren

Es gibt keine Einschränkungen beim Hinzufügen von NPM-Paketen von Drittanbietern zu Stripe-Apps. Sie können Pakete nach Belieben installieren. Angesichts der [Sandbox-iFrame-Einschränkungen](https://docs.stripe.com/stripe-apps/how-ui-extensions-work.md#sandboxed-iframe-limitations) von Erweiterungen der Nutzeroberfläche funktionieren jedoch nicht alle Pakete wie erwartet.

Eine Dienstprogrammbibliothek wie `lodash` kann verwendet werden, da `lodash` keinen DOM-Zugriff erfordert:

```jsx
import { Box, Button } from "@stripe/ui-extension-sdk/ui";
import { useState } from "react";
import kebabCase from "lodash/kebabCase";

const text = "A note to the user";

const App = () => {
  const [isKebabCase, setIsKebabCase] = useState(false);
  return (
    <>
      {/* This will work ✅ */}
      <Box>{isKebabCase ? kebabCase(text) : text}</Box>
      <Button
        onPress={() => {
          setIsKebabCase(!isKebabCase);
        }}
      >
        Toggle kebab-case
      </Button>
    </>
  );
};
```

Eine Formularbibliothek wie `react-hook-form` kann nicht verwendet werden, da `react-hook-form` Refs zur Verwaltung des Formularstatus verwendet:

```jsx
import { TextField } from "@stripe/ui-extension-sdk/ui";
import { useForm } from "react-hook-form";

const App = () => {
  const { register } = useForm();
  const { onChange, name, ref } = register("firstName");
  return (
    <TextField
      label="First name"
      placeholder="Enter your name"
      name={name}
      onChange={onChange}
      // This doesn't work ❌
      ref={ref}
    />
  );
};
```

## See also

- [Ansichten entwickeln und testen](https://docs.stripe.com/stripe-apps/build-ui.md)
- [Ganzseitige Apps](https://docs.stripe.com/stripe-apps/patterns/full-page-apps.md)
- [App gestalten](https://docs.stripe.com/stripe-apps/design.md)
- [App gestalten](https://docs.stripe.com/stripe-apps/style.md)
- [Verteilungsoptionen](https://docs.stripe.com/stripe-apps/distribution-options.md)
