コンポーネント工房:Reflexライブラリ100
·
講座の概要
レイアウト
·
Box
·
Flex
·
Stack
·
Grid
·
Container
·
Section
·
Center
·
Spacer
·
Separator
·
Card
·
Inset
·
Aspect Ratio
·
Fragment
タイポグラフィ
·
Text
·
Heading
·
Link
·
Code
·
Markdown
·
Blockquote
·
Quote
·
Strong
·
Em
·
Kbd
フォーム
·
Button
·
Input
·
Text Area
·
Checkbox
·
Switch
·
Radio Group
·
Select
·
Slider
·
Form
·
Upload
データ表示
·
Badge
·
Avatar
·
Icon
·
Callout
·
List
·
Data List
·
Code Block
·
Progress
·
Spinner
·
Scroll Area
·
Moment
開閉と切り替え
·
Tabs
·
Accordion
·
Segmented Control
オーバーレイ
·
Tooltip
·
Popover
·
Hover Card
·
Dialog
·
Alert Dialog
·
Drawer
·
Dropdown Menu
·
Context Menu
·
Toast
動的レンダリング
·
Cond
·
Match
·
Foreach
·
Auto Scroll
メディア
·
Image
·
Audio
·
Video
表とデータグリッド
·
Table
·
Data Table
·
Data Editor
チャート
·
Area Chart
·
Bar Chart
·
Line Chart
·
Composed Chart
·
Pie Chart
·
Radar Chart
·
Radial Bar Chart
·
Scatter Chart
·
Funnel Chart
·
Treemap
·
Sankey Chart
·
Error Bar
チャートの部品
·
Axis
·
Cartesian Grid
·
Chart Tooltip
·
Legend
·
Label
·
Reference
·
Brush
ほかのチャートライブラリ
·
Plotly
·
Pyplot
HTML要素
·
HTML
·
HTML Text
·
HTML Layout
·
HTML Forms
·
HTML Media
·
HTML Tables
·
SVG
そのほかの道具
·
Theme
·
Skeleton
·
Clipboard
·
Html Embed
·
Script
·
Memo
工房の注文書
カートの数が変わるたびに隣の商品カードの一覧まで再描画されないよう、カードだけを切り出してください。
@rx.memoは関数をメモ化された React コンポーネントに変えます。コンパイラーはその関数を独立したモジュールとして書き出し、React のmemoが、宣言されたプロパティが変わったときだけ再描画します。
描画が重く、State のごく一部にしか依存しないサブツリーに使います。このページの@rx.memo関数は、すべて例のコードの上側(State のソース)で定義されています。
Count: 0
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はlabelが変わったときだけ再描画されます。Incrementでcountを増やしても、このコンポーネントは再描画されません。
rx.memoには目に見えるプロパティがないので、実習台の代わりに State で動く例で見ていきます。プロパティには State の Var をそのまま渡せ、その Var が変わると再描画されます。
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),
)呼び出す側で State をプロパティに結び付けても、State がページに引き込まれることはありません。コンパイラーがその呼び出しを自動生成のラッパーコンポーネントに移し、ラッパーが必要な State のフックを持ちます。
State が変わるとラッパーは再描画されますが、プロパティの値が実際には変わっていなければ、React のmemoがそこで止めます。重いコンポーネントには本当に必要な Var だけを渡すのがコツです。
| 型注釈 | 値 | 既定値 | 用途 |
|---|---|---|---|
rx.Var[T] | str, int, TypedDict … | なし | 普通のプロパティ。関数の中では Var として扱う |
rx.RestProp | パラメーター1つまで | なし | 宣言していない残りのプロパティをそのまま渡す(JSX の...rest) |
children: rx.Var[rx.Component] | 名前は必ずchildren | なし | 呼び出す側の子コンポーネント |
| 既定値 | rx.EMPTY_VAR_STR rx.EMPTY_VAR_INT rx.EMPTY_VAR_COMPONENT | なし | 既定値も Var でなければならない。空文字列・0・空のコンポーネント |
| 呼び出し | memo_fn(title="...") | プロパティは名前(キーワード)でだけ渡す | |
wrapper= | Var またはNone | React のmemo | 包む JavaScript の関数 |
リストの項目ごとにメモコンポーネントを描くには、呼び出しをラムダで包み、プロパティをキーワードで渡します。項目を区別するkeyも一緒に渡します。
keyが実際の要素に届くよう、rx.RestPropのパラメーターを宣言して、返すコンポーネントに展開して入れます。keyをシグネチャーに直接宣言してはいけません。
関数の中のtaskは dict ではなく Var なので、task["name"]のように取り出して使うことはできますが、ループしたり.keys()のような dict のメソッドを呼んだりはできません。
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"),
)rx.RestPropは宣言していないプロパティを受け取って、そのまま渡します。基本のコンポーネントに見た目だけを付けた薄いラッパーを作るとき、プロパティをひとつずつ宣言し直さずに済みます。
restは中身をのぞけない値として扱い、使うコンポーネントに位置引数として渡します。on_click、id、titleがすべて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",
).mergeでrestに別の dict を合わせることができ、rest.get(...)で値を取り出して Var の演算ができます。ただし値はコンパイル時にはわからないので、Python のifで分岐することはできません。
Reflex 0.10 で公式ドキュメントの例をそのまま使うと、2か所が食い違います。restはブラウザで使う JavaScript の名前(className)を持っているので"class_name"では取り出せず、
rest.get(...)の結果は型が決まっていない Var なので、そのまま+ " ..."をするとエラーになります。そこで"className"で取り出して合わせ、.to(str)で文字列の 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",
)rx.RestPropはプロパティを実行時に渡すので、対象コンポーネントのcreate()がビルド時に使う値は運べません。
rx.linkのis_external、rx.iconのtag、自作のcreate()が受け取るキーワードがそうです。restで送ると、ただの属性や CSS になって何の効果もありません。
こうした値は別にrx.Var[...]のパラメーターとして受け取り、関数の本体でcreate()に直接渡します。
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")名前がchildrenでrx.Var[rx.Component]と注釈したパラメーターが、呼び出す側の子要素を受け取ります。子要素は位置引数で、残りのプロパティはキーワードで渡します。
noteのように既定値がrx.EMPTY_VAR_STRのプロパティは省略できます。
Body copy goes here.
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",
)戻り値の注釈をrx.Var[T]にすると、コンポーネントではなく普通の JavaScript の関数が作られ、呼び出した場所はページに差し込んで使える Var になります。
このようなメモの本体はコンパイル時に一度だけ実行されるので、使えるのは Var の演算だけです。フックも、Var を見て行う Python の分岐もできません。
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",
)コンパイルされた関数コンポーネントは、既定で React のmemoに包まれます。wrapper=に別の呼び出し可能な Var を渡せばそれで包み、wrapper=Noneなら包まない関数コンポーネントをそのまま書き出します。
下はwrapper=Noneの例です。コンポーネントを返すメモにだけ使え、Var を返すメモに渡すと拒否されます。
@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",
)ほかのライブラリの関数で包むときは、FunctionStringVarに import を載せて送ります。下の例はmobx-react-liteパッケージが必要なので、コードとしてだけ示します。
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)使うとき:描画が重く、結果が少数のプロパティで決まり、頻繁に変わる祖先のせいで無駄に再描画されるとき。
使わないとき:軽いコンポーネントで、管理のコストのほうが高くつくとき。あるいはプロパティがどのみち毎回変わり、memoが一度もスキップできないとき。
以前のrx.memoはtitle: strのような普通の型注釈を受け取っていました。いまはrx.Var[...]が必要です。古い名前のrx._x.memoは新しいrx.memoにつながり、一度だけ案内のメッセージを出します。
# before
@rx.memo
def card(title: str) -> rx.Component: ...
# now
@rx.memo
def card(title: rx.Var[str]) -> rx.Component: ...| 引数 | 型 | 用途 |
|---|---|---|
component_fn | Callable[..., rx.Component | rx.Var] | メモにする関数。パラメーターはすべてrx.Var[...]またはrx.RestProp |
wrapper | rx.Var | None | 包む JS の関数。既定は React のmemo、Noneなら包まない。コンポーネントを返すメモ専用 |
rx.memo(fn)のように直接使うか、rx.memo(wrapper=...)(fn)のようにキーワードだけを渡してデコレーターを作ります。
商品カードを@rx.memoで切り出しました。カードはproductだけを受け取るので、Add oneでカートの数が変わってもカードは再描画されません。
width="100%"はrestに乗ってカードに届きます。
Desk lamp
$39Notebook
$6Fountain pen
$58from 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",
)工房ノート
@rx.memo は、宣言したプロパティが変わったときだけ再描画する React コンポーネントを作る。
パラメーターはすべて rx.Var[T] または rx.RestProp(1つまで)。子要素は children: rx.Var[rx.Component]。
呼び出しはキーワードで。既定値は rx.EMPTY_VAR_STR のような Var。foreach では key を rest で。
rest は実行時に渡すプロパティだけ。create() がビルド時に使う値は別のパラメーターで。
-> rx.Var[T] なら JS の関数になる。wrapper=None は memo で包まない。
新しい授業・講座のお知らせを受け取る
この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。
R
ReflexKR
触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。
ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する
アフィリエイトと支援について© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
質問と回答
この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。