tutorial_261019/tutorial_261019.py)import dataclasses
from collections import Counter
import reflex as rx
@dataclasses.dataclass
class User:
name: str
email: str
gender: str
class State(rx.State):
users: list[User] = [
User(name="Danilo Sousa", email="danilo@example.com", gender="Male"),
User(name="Zahra Ambessa", email="zahra@example.com", gender="Female"),
]
users_for_graph: list[dict] = []
add_user_dialog_open: bool = False
@rx.event
def set_add_user_dialog_open(self, open_: bool):
self.add_user_dialog_open = open_
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))
self.transform_data()
self.add_user_dialog_open = False
@rx.event
def transform_data(self):
gender_counts = Counter(user.gender for user in self.users)
self.users_for_graph = [
{"name": gender_group, "value": count} for gender_group, count in gender_counts.items()
]
def show_user(user: User):
return rx.table.row(
rx.table.cell(user.name),
rx.table.cell(user.email),
rx.table.cell(user.gender),
style={"_hover": {"bg": rx.color("gray", 3)}},
align="center",
)
def add_customer_button() -> rx.Component:
return rx.dialog.root(
rx.dialog.trigger(
rx.button(
rx.icon("plus", size=18),
rx.text("Add User", size="2"),
),
),
rx.dialog.content(
rx.dialog.title(
"Add New User",
),
rx.dialog.description(
"Fill the form with the user's info",
),
rx.form(
rx.flex(
rx.input(placeholder="User Name", name="name", required=True),
rx.input(
placeholder="user@reflex.dev",
name="email",
),
rx.select(
["Male", "Female"],
placeholder="Select gender",
default_value="Male",
name="gender",
custom_attrs={"aria-label": "Gender"},
),
rx.flex(
rx.dialog.close(
rx.button(
"Cancel",
type="button",
variant="soft",
color_scheme="gray",
),
),
rx.button("Submit", type="submit"),
spacing="3",
justify="end",
),
direction="column",
spacing="4",
),
on_submit=State.add_user,
reset_on_submit=False,
),
max_width="450px",
),
open=State.add_user_dialog_open,
on_open_change=State.set_add_user_dialog_open,
)
def graph():
return rx.recharts.bar_chart(
rx.recharts.cartesian_grid(
horizontal=False, stroke_dasharray="3 3", stroke=rx.color("slate", 4)
),
rx.recharts.x_axis(
type_="number",
allow_decimals=False,
axis_line=False,
tick_line=False,
domain=[0, "dataMax + 1"],
),
rx.recharts.y_axis(
type_="category",
data_key="name",
axis_line=False,
tick_line=False,
width=64,
),
rx.recharts.bar(
data_key="value",
fill=rx.color("accent", 9),
radius=[0, 6, 6, 0],
bar_size=28,
),
data=State.users_for_graph,
layout="vertical",
width="100%",
height=160,
margin={"top": 0, "right": 16, "bottom": 0, "left": 0},
)
def index() -> rx.Component:
return rx.box(
rx.box(
rx.box(
rx.box(
rx.heading("Customers", size="6", as_="h3", margin="0"),
rx.badge(
State.users.length(),
variant="soft",
color_scheme="gray",
radius="full",
),
display="flex",
align_items="center",
gap="12px",
),
rx.text(
"Your customer directory and audience breakdown.",
size="2",
color=rx.color("slate", 11),
margin_top="6px",
),
),
add_customer_button(),
display="flex",
align_items="center",
justify_content="space-between",
flex_wrap="wrap",
gap="16px",
padding="24px",
),
rx.box(
rx.table.root(
rx.table.header(
rx.table.row(
rx.table.column_header_cell("Name"),
rx.table.column_header_cell("Email"),
rx.table.column_header_cell("Gender"),
)
),
rx.table.body(rx.foreach(State.users, show_user)),
variant="ghost",
size="3",
width="100%",
),
overflow_x="auto",
width="100%",
border_top=f"1px solid {rx.color('slate', 4)}",
border_bottom=f"1px solid {rx.color('slate', 4)}",
padding_x="12px",
),
rx.box(
rx.text("Audience breakdown", size="3", weight="bold"),
rx.text(
"Customers by gender",
size="2",
color=rx.color("slate", 11),
margin_top="4px",
margin_bottom="20px",
),
graph(),
padding="24px",
),
width="100%",
min_width="0",
max_width="64em",
margin_x="auto",
margin_y="24px",
border=f"1px solid {rx.color('slate', 5)}",
border_radius="16px",
background=rx.color("slate", 1),
overflow="hidden",
box_shadow="0 4px 24px rgba(0, 0, 0, 0.03)",
style={
"& *": {"box_sizing": "border-box"},
"& p, & h3": {"text_align": "left"},
},
)
app = rx.App()
app.add_page(
index,
title="Customer Data App",
description="A simple app to manage customer data.",
on_load=State.transform_data,
)| 用語 | 意味 |
|---|---|
| フルスタック | 画面(フロントエンド)とサーバー(バックエンド)の両方を扱う開発 |
| コンポーネント | 画面を構成する部品。Reflexではrx.xxx(...)という関数呼び出し1つ |
| props | コンポーネントに渡す設定値。キーワード引数で渡す |
| State | アプリのデータとイベントハンドラをまとめたクラス。サーバー上にセッションごとに存在する |
| Var | 画面のコードの中でStateの変数を指す式。値はブラウザで埋められる |
| computed var | @rx.varで宣言する、ほかの状態から計算される変数 |
| イベントハンドラ | @rx.eventを付けたStateのメソッド。ユーザーの操作に反応してサーバーで実行される |
| 制御コンポーネント | 画面要素の値や状態をStateが決める方式(open=、value=) |
| コンパイル時 | アプリの起動時に、画面の関数がReactのコードに変換されるタイミング |
| 実行時 | アプリが動いていて、ユーザーが操作しているタイミング。イベントハンドラが実行される |
| WebSocket | ブラウザとサーバーが接続を開いたままにして、双方向にメッセージをやり取りする通信方式 |
| ホットリロード | コードを保存すると、サーバーを止めずに自動で再コンパイルして反映する機能 |
1時限を1本の記事にすると、全6回のシリーズになります。
| 回 | タイトル例 | 扱う時限 |
|---|---|---|
| 1 | Pythonだけでウェブアプリを作る、Reflex入門(+Windowsのエラー対処) | 1限目 |
| 2 | HTMLなしで画面を描く:Reflexのコンポーネントとスタイル | 2限目 |
| 3 | Reflexでlen()が使えない理由:StateとVar | 3限目 |
| 4 | フォームとダイアログでデータを追加する | 4限目 |
| 5 | Pythonで集計してチャートを描く | 5限目 |
| 6 | Reflex初心者が必ず出会うエラー8選 | 6限目 6-2 |
講師メモの引用ブロックは、本文の説明として書き直すか、「ヒント」ボックスに置き換えます。len()のエラー、4限目のCancelボタンのtypeの書き忘れ、閉じないダイアログは、わざと再現して見せる価値が大きいポイントです。新しい授業・講座のお知らせを受け取る
この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。
R
ReflexKR
触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。
ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する
アフィリエイトと支援について© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
質問と回答
この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。