Der Spickzettel zur React-Schulung für Atlassian Forge — von modernem JavaScript bis zum Resolver in der Forge-Sandbox. Die Forge-Plattform ändert sich; für Manifest-Schlüssel und CLI-Flags bleibt die Atlassian-Doku maßgeblich.
TypeScript: interface oder type?
| Aspekt | interface | type |
|---|---|---|
| Erweitern | extends | & (Intersection) |
| Union-Types | nein | ja |
| Deklarations-Merge | ja | nein |
| Typischer Einsatz | Objektformen | Unions, Primitive |
Faustregel: Objektformen als interface, alles Übrige als type.
interface User { id: number; name: string; role?: Role }
type ID = string | number
function first<T>(items: T[]): T { return items[0] }
first(["a", "b"]) // T = string (hergeleitet)
In React typisiert man Props und Events explizit:
interface Props { title: string; count?: number }
function Badge({ title, count = 0 }: Props) { … }
const [user, setUser] = useState<User | null>(null)
type Ev = React.ChangeEvent<HTMLInputElement>
Modul: TypeScript für React
React-Grundmuster
// Props sind read-only, children ist eine normale Prop
function Card({ title, children }) {
return <section><h2>{title}</h2>{children}</section>;
}
// State: Paar aus Wert und Setter
const [count, setCount] = useState(0);
// Liste: stabile ID als key, nie der Array-Index
{items.map((item) => <li key={item.id}>{item.text}</li>)}
// Kontrolliertes Feld: value und onChange gehören zusammen
<input value={name} onChange={(e) => setName(e.target.value)} />
Drei Muster für bedingtes Rendern — if für den frühen Ausstieg, && für
„zeige nur wenn”, der Ternär für „entweder/oder”:
if (loading) return <p>Lädt…</p>;
{user && <span>Hi {user.name}</span>}
{user ? <Logout /> : <Login />}
Modul: React-Grundlagen
Wo gehört der State hin?
| Situation | Passende Ebene |
|---|---|
| Nur eine Komponente nutzt ihn | Lokaler useState |
| Zwei Geschwister teilen ihn | In den Vorfahren heben |
| Ganzer Teilbaum braucht ihn | Context API |
| Serverdaten, Caching, Sync | Data-Library (z. B. React Query) |
Erst lokal, dann heben, dann Context — in dieser Reihenfolge.
| Pattern | Wofür |
|---|---|
| Lifting State Up | Geschwister synchron halten |
| Controlled Component | Formularwerte in React kontrollieren |
Composition (children) | Flexible, wiederverwendbare Layouts |
| Custom Hook | Logik kapseln und wiederverwenden |
function useToggle(start = false) {
const [on, setOn] = useState(start);
return [on, () => setOn((v) => !v)];
}
Ein Custom Hook beginnt mit use — sonst greifen die Hook-Regeln nicht. Er
kapselt Logik, kein JSX.
Modul: State Management in React
Projektstruktur
src/
features/
todos/
TodoListe.jsx
useTodos.js
todos.api.js
components/ # geteilte UI
lib/ # Hilfsfunktionen
Bei kleinen Projekten reicht typ-basiert; feature-basiert lohnt ab mittlerer Größe. Struktur nicht dogmatisch erzwingen, sondern mitwachsen lassen.
Modul: React-Projektstruktur und Best Practices
Atlassian Forge
manifest.yml ist die zentrale Wahrheit über Module, Rechte und Ressourcen:
modules:
jira:issuePanel:
- key: hallo-panel
resource: main
title: Hallo Forge
resources:
- key: main
path: src/frontend/index.jsx
npm install -g @forge/cli
forge login # E-Mail + API-Token, nicht das Passwort
forge whoami
forge lint
forge deploy -e development
forge install --site meine.atlassian.net
forge tunnel # lokaler Code, Hot Reload
Im UI Kit gibt es keine HTML-Tags — nur Komponenten aus @forge/react:
import ForgeReconciler, { Button, useState } from "@forge/react";
const App = () => {
const [n, setN] = useState(0);
return <Button onClick={() => setN(n + 1)}>Klicks: {n}</Button>;
};
ForgeReconciler.render(<App />);
import Resolver from "@forge/resolver";
import { storage } from "@forge/api";
const resolver = new Resolver();
resolver.define("save", ({ payload }) => storage.set("note", payload.text));
export const handler = resolver.getDefinitions();
Der invoke-Name im Frontend muss exakt dem resolver.define-Namen
entsprechen.
Modul: React im Atlassian-Forge-Umfeld
Typische Fallen
JavaScript / TypeScript
==erzwingt Typumwandlung — im Zweifel immer===.- Arrow Functions haben kein eigenes
this— als Objektmethode unbrauchbar. - Vergessenes
awaitliefert ein Promise statt des Werts. anybreitet sich aus und hebelt die Prüfung aus;@ts-ignoreist keine Dauerlösung.tscerzeugt trotz Typfehler Output, solangenoEmitOnErrorfehlt.- Dateien mit JSX brauchen
.tsx, nicht.ts.
React
- Der Array-Index als
keybricht bei Umsortieren oder Einfügen. {count && …}rendert bei 0 die Zahl 0 — bessercount > 0prüfen.onClick={handleClick()}ruft sofort auf; die Klammern gehören weg.- State direkt mutieren (
count++) löst kein Re-Render aus. Objekte und Arrays neu erzeugen — und daran denken, dass Spread nur flach kopiert. - State-Updates sind asynchron. Mehrere Updates im selben Event brauchen die
Updater-Form
setX(v => …). - Hooks nur auf oberster Ebene — nie in Schleifen oder Bedingungen.
- Fehlende Dependencies im Effekt führen zu veralteten Werten; fehlendes Cleanup zu Memory-Leaks und Doppel-Timern.
- Startwert
""stattundefined, sonst wechselt das Feld zwischen uncontrolled und controlled. - Error Boundaries greifen nicht bei async-Code oder Event-Handlern.
Forge
forge deployvergessen →forge installnutzt den alten Stand.- Neue Rechte (z. B.
storage) müssen ins Manifest und neu installiert werden; auch ausgehende Netzwerkaufrufe brauchen eine Freigabe. manifest.ymlist YAML — Leerzeichen einrücken, keine Tabs.resource.pathzeigt ins Leere → das Frontend lädt nicht.storage.setist asynchron — Promise zurückgeben oderawait.- Nicht jede Node-Bibliothek läuft in der Forge-Sandbox.
- UI Kit und Custom UI nicht im selben Modul vermischen.