R

ReflexKR

Reflex-Dokumentation

Die Komponentenwerkstatt: Reflex' 100 Bibliotheksbausteine

·

Kursüberblick

Layout

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

Typografie

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

Formulare

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

Datenanzeige

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

Auf- und Zuklappen

·

Tabs

·

Accordion

·

Segmented Control

Overlays

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

Dynamisches Rendern

·

Cond

·

Match

·

Foreach

·

Auto Scroll

Medien

·

Image

·

Audio

·

Video

Tabellen und Datengrids

·

Table

·

Data Table

·

Data Editor

Diagramme

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

Diagrammbausteine

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

Andere Diagrammbibliotheken

·

Plotly

·

Pyplot

HTML-Elemente

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

Weitere Werkzeuge

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

Die Komponentenwerkstatt: Reflex' 100 Bibliotheksbausteine
Weitere Werkzeuge

Memo

rx.memorx.RestProprx.EMPTY_VAR_STR

Offizielle Doku

Werkstattauftrag

Bitte lös die Produktkarten heraus, damit nicht jedes Mal die ganze Kartenliste daneben neu gezeichnet wird, wenn sich die Zahl im Warenkorb ändert.

@rx.memo macht aus einer Funktion eine memoisierte React-Komponente. Der Compiler exportiert die Funktion als eigenes Modul, und Reacts memo zeichnet sie nur neu, wenn sich deklarierte Props ändern. Du nimmst es für Teilbäume, die teuer zu zeichnen sind und nur von einem kleinen Teil des Zustands abhängen. Alle @rx.memo-Funktionen dieser Seite sind oberhalb des Beispielcodes (in der State-Quelle) definiert.

Count: 0

Memoized Component

This component only re-renders when props change.

class MemoDemoState(rx.State):
    count: int = 0

    @rx.event
    def increment(self):
        self.count += 1


@rx.memo
def memo_expensive_component(label: rx.Var[str]) -> rx.Component:
    return rx.vstack(
        rx.heading(label, as_="h2", size="4"),
        rx.text("This component only re-renders when props change."),
        rx.divider(),
    )


rx.vstack(
    rx.text("Count: ", MemoDemoState.count),
    rx.button("Increment", on_click=MemoDemoState.increment),
    memo_expensive_component(label="Memoized Component"),
)

memo_expensive_component wird nur neu gezeichnet, wenn sich label ändert. Erhöhst du count mit Increment, wird diese Komponente nicht neu gezeichnet.

State-Werte als Props

rx.memo hat keine sichtbaren Props, daher gibt es statt eines Experimentiertischs ein Beispiel, das sich über den State bewegt. Props kannst du direkt State-Vars übergeben; ändert sich die Var, wird neu gezeichnet.

Hello, World

class MemoGreetState(rx.State):
    name: str = "World"

    @rx.event
    def change_name(self, value: str):
        self.name = value


@rx.memo
def memo_greeting(name: rx.Var[str]) -> rx.Component:
    return rx.heading("Hello, " + name, as_="h2", size="5")


rx.vstack(
    memo_greeting(name=MemoGreetState.name),
    rx.input(value=MemoGreetState.name, on_change=MemoGreetState.change_name),
)

Auch wenn du an der Aufrufstelle State an Props bindest, wird der State nicht in die Seite hineingezogen. Der Compiler verlagert den Aufruf in eine automatisch erzeugte Wrapper-Komponente, und der Wrapper hält die nötigen State-Hooks. Ändert sich der State, wird der Wrapper neu gezeichnet, doch wenn sich der Prop-Wert gar nicht geändert hat, stoppt Reacts memo dort. Der Trick: einer teuren Komponente nur die wirklich nötigen Vars übergeben.

Regeln

AnnotationWerteStandardVerwendung
rx.Var[T]str, int, TypedDict …keinerNormale Prop. In der Funktion als Var behandeln
rx.RestProphöchstens ein ParameterkeinerReicht nicht deklarierte restliche Props unverändert weiter (...rest in JSX)
children: rx.Var[rx.Component]Name muss children seinkeinerKindkomponenten des Aufrufers
Standardwerterx.EMPTY_VAR_STR rx.EMPTY_VAR_INT rx.EMPTY_VAR_COMPONENTkeinerAuch Standardwerte müssen Vars sein. Leerer String · 0 · leere Komponente
Aufrufmemo_fn(title="...")Props nur per Name (Schlüsselwort) übergeben
wrapper=Var oder NoneReacts memoJavaScript-Funktion, die die Komponente umhüllt

Mit rx.foreach

Um für jeden Listeneintrag eine Memo-Komponente zu zeichnen, packst du den Aufruf in ein Lambda und übergibst die Props per Schlüsselwort. Den key, der die Einträge unterscheidet, gibst du mit. Damit key das eigentliche Element erreicht, deklarierst du einen rx.RestProp-Parameter und breitest ihn in der zurückgegebenen Komponente aus. key direkt in der Signatur zu deklarieren ist nicht erlaubt. task in der Funktion ist kein dict, sondern eine Var: Du kannst Werte wie task["name"] herausholen, aber nicht darüber iterieren oder dict-Methoden wie .keys() aufrufen.

Write docs

Review PR

from typing import TypedDict


class MemoTask(TypedDict):
    id: str
    name: str


class MemoTaskState(rx.State):
    tasks: list[MemoTask] = [
        {"id": "1", "name": "Write docs"},
        {"id": "2", "name": "Review PR"},
    ]

    @rx.event
    def add_task(self):
        n = len(self.tasks) + 1
        self.tasks = [*self.tasks, {"id": str(n), "name": f"Task {n}"}][-6:]


@rx.memo
def memo_task_card(rest: rx.RestProp, *, task: rx.Var[MemoTask]) -> rx.Component:
    return rx.card(rx.text(task["name"]), rest)


rx.vstack(
    rx.foreach(
        MemoTaskState.tasks,
        lambda task: memo_task_card(task=task, key=task["id"]),
    ),
    rx.button("Add task", on_click=MemoTaskState.add_task, variant="soft"),
)

Props per rx.RestProp weiterreichen

rx.RestProp nimmt nicht deklarierte Props entgegen und reicht sie unverändert weiter. So baust du dünne Hüllen, die einer Basiskomponente nur ein Aussehen geben, ohne jede Prop neu zu deklarieren. Betrachte rest als undurchsichtigen Wert und übergib ihn der Zielkomponente als Positionsargument. on_click, id und title erreichen den Button alle über rest.

@rx.memo
def memo_pill_button(rest: rx.RestProp, *, label: rx.Var[str]) -> rx.Component:
    return rx.button(label, rest, radius="full", variant="surface")


rx.flex(
    memo_pill_button(label="Save", on_click=rx.toast("Saved"), id="memo_save"),
    memo_pill_button(label="Help", on_click=rx.toast("Help is on the way"), title="Opens help"),
    spacing="3",
)

Mit .merge kannst du ein weiteres dict in rest mischen, und mit rest.get(...) holst du Werte heraus, um mit Vars zu rechnen. Die Werte sind beim Kompilieren aber unbekannt, also kannst du nicht mit Pythons if verzweigen. Übernimmst du in Reflex 0.10 das Beispiel der offiziellen Doku unverändert, passen zwei Stellen nicht. rest enthält die JavaScript-Namen aus dem Browser (className), mit "class_name" kommst du also nicht heran, und das Ergebnis von rest.get(...) ist eine untypisierte Var, ein direktes + " ..." wirft einen Fehler. Deshalb wird mit "className" gelesen und gemischt, und vor dem Verketten macht .to(str) daraus eine String-Var.

@rx.memo
def memo_merged_button(rest: rx.RestProp, *, label: rx.Var[str]) -> rx.Component:
    class_name = rest.get("className", "").to(str) + " font-bold tracking-wide"
    return rx.button(label, rest.merge({"className": class_name}))


rx.flex(
    memo_merged_button(label="Default"),
    memo_merged_button(label="Caller class", class_name="italic"),
    spacing="3",
)

Grenze: Props, die beim Build gebraucht werden

rx.RestProp reicht Props zur Laufzeit weiter und kann daher keine Werte transportieren, die create() der Zielkomponente beim Build verwendet. Dazu gehören is_external bei rx.link, tag bei rx.icon und Schlüsselwörter, die ein selbst geschriebenes create() annimmt. Schickst du sie per rest, werden sie zu bloßen Attributen oder CSS und wirken nicht. Solche Werte nimmst du als eigene rx.Var[...]-Parameter entgegen und übergibst sie im Funktionsrumpf direkt an create().

P: Foo
class MemoPrefixText(rx.el.Span):
    @classmethod
    def create(cls, *children, prefix: rx.Var[str] | str = "", **props) -> rx.Component:
        return super().create(prefix, *children, **props)


# prefix is used by create(), so it is not passed through rest.
# take it as its own parameter and pass it to create() in the memo body.
@rx.memo
def memo_styled_text(rest: rx.RestProp, *, prefix: rx.Var[str]) -> rx.Component:
    return MemoPrefixText.create("Foo", rest, prefix=prefix)


memo_styled_text(prefix="P: ", class_name="italic")

Kinder entgegennehmen

Ein Parameter namens children mit der Annotation rx.Var[rx.Component] nimmt die Kinder des Aufrufers entgegen. Kinder übergibst du als Positionsargumente, die übrigen Props als Schlüsselwörter. Props mit dem Standardwert rx.EMPTY_VAR_STR wie note darfst du weglassen.

Memoized card

Body copy goes here.

With a note

note is optional

@rx.memo
def memo_panel(
    children: rx.Var[rx.Component],
    *,
    title: rx.Var[str],
    note: rx.Var[str] = rx.EMPTY_VAR_STR,
) -> rx.Component:
    return rx.box(
        rx.heading(title, as_="h3", size="3"),
        children,
        rx.text(note, size="1", color_scheme="gray"),
        padding="12px",
        border=f"1px solid {rx.color('gray', 6)}",
        border_radius="8px",
    )


rx.flex(
    memo_panel(rx.text("Body copy goes here."), title="Memoized card"),
    memo_panel(rx.button("Inside", size="1"), title="With a note", note="note is optional"),
    spacing="3",
    wrap="wrap",
)

Eine Var statt einer Komponente zurückgeben

Mit der Rückgabe-Annotation rx.Var[T] entsteht keine Komponente, sondern eine gewöhnliche JavaScript-Funktion, und die Aufrufstelle wird zu einer Var, die du in die Seite einsetzen kannst. Der Rumpf eines solchen Memos läuft einmal beim Kompilieren, daher sind nur Var-Operationen erlaubt. Keine Hooks und keine Python-Verzweigungen anhand von Vars.

USD: $100

class MemoPriceState(rx.State):
    amount: int = 100
    currency: str = "USD"

    @rx.event
    def raise_price(self):
        self.amount += 10


@rx.memo
def memo_format_price(amount: rx.Var[int], currency: rx.Var[str]) -> rx.Var[str]:
    return currency.to(str) + ": $" + amount.to(str)


rx.hstack(
    rx.text(memo_format_price(amount=MemoPriceState.amount, currency=MemoPriceState.currency), weight="bold"),
    rx.button("+10", on_click=MemoPriceState.raise_price, size="1", variant="soft"),
    align="center",
)

Den JavaScript-Wrapper austauschen

Kompilierte Funktionskomponenten werden standardmäßig in Reacts memo gehüllt. Gibst du wrapper= eine andere aufrufbare Var, wird damit gehüllt; mit wrapper=None wird die Funktionskomponente ungehüllt exportiert. Unten ein Beispiel für wrapper=None. Das geht nur bei Memos, die Komponenten zurückgeben; bei Memos, die Vars zurückgeben, wird es abgelehnt.

@rx.memo(wrapper=None)
def memo_swatch(width: rx.Var[int], height: rx.Var[int]) -> rx.Component:
    return rx.box(width=width.to_string() + "px", height=height.to_string() + "px", background=rx.color("indigo", 9), border_radius="4px")


rx.flex(
    memo_swatch(width=40, height=20),
    memo_swatch(width=80, height=20),
    memo_swatch(width=120, height=20),
    spacing="2",
    align="center",
)

Um mit einer Funktion aus einer anderen Bibliothek zu hüllen, gibst du einem FunctionStringVar den Import mit. Das Beispiel unten braucht das Paket mobx-react-lite und steht deshalb nur als Code da.

observer = rx.vars.FunctionStringVar(
    "observer",
    _var_data=rx.vars.VarData(imports={"mobx-react-lite": "observer"}),
)


@rx.memo(wrapper=observer)          # wrap with mobx's observer instead of React's memo
def observed_panel(label: rx.Var[str]) -> rx.Component:
    return rx.text(label)

Wann ja, wann nein

Ja: wenn das Zeichnen teuer ist, das Ergebnis von wenigen Props abhängt und ein sich häufig ändernder Vorfahre unnötiges Neuzeichnen auslöst. Nein: bei leichten Komponenten, bei denen die Buchführung teurer ist, oder wenn sich die Props sowieso jedes Mal ändern und memo nie etwas überspringen kann.

Umstieg vom alten rx.memo

Das frühere rx.memo nahm gewöhnliche Typannotationen wie title: str. Heute braucht es rx.Var[...]. Der alte Name rx._x.memo leitet auf das neue rx.memo weiter und gibt einmal einen Hinweis aus.

# before
@rx.memo
def card(title: str) -> rx.Component: ...


# now
@rx.memo
def card(title: rx.Var[str]) -> rx.Component: ...

API

ArgumentTypVerwendung
component_fnCallable[..., rx.Component | rx.Var]Die zu memoisierende Funktion. Alle Parameter sind rx.Var[...] oder rx.RestProp
wrapperrx.Var | NoneUmhüllende JS-Funktion. Standard ist Reacts memo, None hüllt nicht. Nur bei Memos, die Komponenten zurückgeben

Du verwendest es direkt wie rx.memo(fn) oder erzeugst mit nur Schlüsselwörtern einen Dekorator wie rx.memo(wrapper=...)(fn).

Auftrag erledigt

Die Produktkarte ist per @rx.memo herausgelöst. Die Karte bekommt nur product, daher wird sie nicht neu gezeichnet, wenn sich die Warenkorbzahl über Add one ändert. width="100%" erreicht die Karte über rest.

0

Desk lamp

$39

Notebook

$6

Fountain pen

$58
from typing import TypedDict


class MemoProduct(TypedDict):
    id: str
    name: str
    price: str


class MemoShopState(rx.State):
    cart: int = 0
    products: list[MemoProduct] = [
        {"id": "p1", "name": "Desk lamp", "price": "$39"},
        {"id": "p2", "name": "Notebook", "price": "$6"},
        {"id": "p3", "name": "Fountain pen", "price": "$58"},
    ]

    @rx.event
    def add_one(self):
        self.cart += 1

    @rx.event
    def empty_cart(self):
        self.cart = 0


@rx.memo
def memo_product_card(rest: rx.RestProp, *, product: rx.Var[MemoProduct]) -> rx.Component:
    return rx.card(
        rx.flex(
            rx.icon("package", size=18),
            rx.text(product["name"], weight="bold"),
            rx.spacer(),
            rx.badge(product["price"], variant="soft"),
            spacing="3",
            align="center",
        ),
        rest,
    )


rx.vstack(
    rx.hstack(
        rx.badge(rx.icon("shopping-cart", size=14), MemoShopState.cart, size="2"),
        rx.button("Add one", on_click=MemoShopState.add_one, size="1"),
        rx.button("Empty", on_click=MemoShopState.empty_cart, size="1", variant="soft", color_scheme="gray"),
        align="center",
    ),
    rx.foreach(
        MemoShopState.products,
        lambda p: memo_product_card(product=p, key=p["id"], width="100%"),
    ),
    width="100%",
    max_width="380px",
)

Werkstattnotizen

  • @rx.memo erzeugt eine React-Komponente, die nur neu zeichnet, wenn sich deklarierte Props ändern.

  • Alle Parameter sind rx.Var[T] oder rx.RestProp (höchstens einer). Kinder per children: rx.Var[rx.Component].

  • Aufruf per Schlüsselwort. Standardwerte sind Vars wie rx.EMPTY_VAR_STR. In foreach geht key über rest.

  • rest trägt nur Laufzeit-Props. Werte, die create() beim Build braucht, als eigene Parameter.

  • Mit -> rx.Var[T] entsteht eine JS-Funktion. wrapper=None hüllt nicht in memo.

← Zurück

Weitere Werkzeuge · Script

Über neue Lektionen informiert werden

Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

R

ReflexKR

Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.

ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee

Hinweis zu Affiliate-Links
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex