---
title: "Reaktivität — zenit Zig UI Doku"
description: "Halten Sie Fakten in Signals, leiten Sie mit Memos ab und synchronisieren Sie die Außenwelt mit Effects."
url: https://zenit.z.express/de/docs/guide/reactivity
language: de
alternate_en: https://zenit.z.express/docs/guide/reactivity.md
alternate_zh: https://zenit.z.express/zh/docs/guide/reactivity.md
alternate_es: https://zenit.z.express/es/docs/guide/reactivity.md
alternate_ja: https://zenit.z.express/ja/docs/guide/reactivity.md
alternate_ko: https://zenit.z.express/ko/docs/guide/reactivity.md
alternate_fr: https://zenit.z.express/fr/docs/guide/reactivity.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Reaktivität

Halten Sie Fakten in Signals, leiten Sie mit Memos ab und synchronisieren Sie die Außenwelt mit Effects.

## Drei Primitive

**Signal**

Eine beschreibbare Quelle der Wahrheit. Lesen mit `get()`, schreiben mit `set(v)`; das Lesen registriert eine Abhängigkeit in der aktuellen Berechnung.

**Memo**

Wird lazy aus anderen reaktiven Werten berechnet; nutzt das letzte Ergebnis wieder, solange die Eingaben gleich bleiben, und benachrichtigt niemanden, wenn der Wert gleich ist.

**Effect**

Führt einen Seiteneffekt aus, wenn sich seine Abhängigkeiten ändern – einen Knoten aktualisieren, loggen, ein externes System synchronisieren.

## Datenfluss von Anfang bis Ende

`ui.textFmt` abonniert die übergebenen Signals / Memos und formatiert bei Änderungen nur diesen einen Textknoten neu. Ein Memo erhält seine Abhängigkeiten über ein explizites Kontext-Struct.

`counter.zig`

```zig
const count = try scope.createSignal(u32, 0);

const doubled = try scope.createMemo(u32, .{ .count = count }, struct {
    fn compute(ctx: anytype) u32 {
        return ctx.count.get() * 2;
    }
}.compute);

try root.appendChild(cx.allocator, try ui.textFmt(
    cx,
    scope,
    "count = {d}, doubled = {d}",
    .{ count, doubled },
    .{ .color = cx.tokens.color.fg_primary },
));
```

DATA FLOW

Ein set läuft die Abhängigkeitskanten entlang – Signal → Memo → textFmt – und nur der abonnierte Textknoten wird render-dirty markiert.

## Signals statt Diffing

zenit behält einen echten UI-Baum für Layout, Hit-Testing und Zeichnen, aber Updates laufen nicht über „Komponenten neu ausführen → Kandidatenbaum bauen → alles diffen“. Memos und Effects, die ein Signal lesen, registrieren eine Abhängigkeit; `set` benachrichtigt genau diese Subscriber, die die betroffenen Knoten als layout- oder render-dirty markieren.

SIGNAL vs DIFF

Der Baum bleibt; was wegfällt, ist das Neuaufbauen eines Kandidatenbaums und die Suche nach Unterschieden bei jeder Änderung.

[Video](https://zenit.z.express/media/reactive-counter.mp4?v=635ee40b41)

Die echte Reactive Counter.app: Jedes +1 aktualisiert count und doubled gemeinsam bis 3 / 6, dann setzt Reset beide Subscriber auf null.

## Aus Events aktualisieren

Komponenten-Callbacks sind `HandlerRef`s. Halten Sie mit `cx.bindState` ein kleines Struct mit dem Signal-Zeiger und machen Sie mit `cx.on` eine seiner Methoden zum Callback.

`handlers.zig`

```zig
const Bindings = struct {
    count: *ui.Signal(u32),

    fn increment(self: *Bindings) void {
        self.count.set(self.count.get() + 1);
    }
};

const bindings = try cx.bindState(Bindings, .{ .count = count });
const on_click = cx.on(Bindings, bindings, Bindings.increment);

const plus = try ui.widgets.Button(.{
    .label = "+1",
    .on_click = on_click,
}).mount(scope, cx);
```

## Lesen und abonnieren

| API | Funktion |
| --- | --- |
| `signal.get()` | Liest und registriert eine Abhängigkeit im aktuellen Memo / Effect |
| `signal.peek()` | Liest ohne Abonnement – praktisch für Snapshots in Handlern |
| `signal.set(v)` | Schreibt und benachrichtigt Subscriber |
| `signal.update(fn)` | Berechnet den nächsten Wert aus dem vorherigen und schreibt ihn |
| `scope.createEffect(ctx, fn)` | Führt bei Änderung einen Seiteneffekt aus; wird mit seinem Scope freigegeben |

`effects.zig`

```zig
// Effect: sync a value to something outside the reactive graph.
try scope.createEffect(.{ .count = count }, struct {
    fn run(ctx: anytype) void {
        std.log.info("count is now {d}", .{ctx.count.get()});
    }
}.run);

// peek(): read without subscribing (no dependency is recorded).
const snapshot = count.peek();

// update(): read-modify-write in one call.
count.update(struct {
    fn inc(v: u32) u32 {
        return v + 1;
    }
}.inc);
```

## Faustregeln

-   **Ein Fakt, ein Signal.** Halten Sie denselben State nicht zugleich in einem normalen Feld und in einem Signal.
    
-   **Ableitbare Werte sind Memos.** Synchronisieren Sie abgeleitete Felder nicht in jedem Handler von Hand.
    
-   **Effects synchronisieren nur.** Schreiben Sie nie bedingungslos in eine Abhängigkeit des Effects selbst zurück – das ist eine Schleife.
    
-   **Lebensdauern gehören zu einem Scope.** Wird der Scope einer Seite freigegeben, gehen seine Signals, Memos und Effects mit.
    

> TIP
> 
> **Statische UI braucht keine Reaktivität.** Mounten Sie Text und Struktur, die sich nie ändern, direkt. Reaktive Primitive lösen sich ändernde Daten – sie sind keine Hülle, die jeder Knoten braucht.
