この時限を終えると、「Add User」ボタンでダイアログを開き、フォームを送信するとテーブルに顧客が追加されて、ダイアログが自動的に閉じるようになります。画面がユーザーの操作に反応する、最初にして最も重要なステップです。
イベントハンドラとは、Stateの中に定義し、@rx.eventを付けたメソッドです。状態を変更できるのはイベントハンドラだけです。
class State(rx.State):
users: list[User] = [...]
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))self.usersはVarではなく実際のリストなので、append、len、if、for、さらには外部ライブラリまで自由に使えます。self.usersを変更するとReflexがその変化を検知してブラウザに送り、テーブルとバッジが自動的に再描画されます。画面を直接書き換えるコードはありません。User(**form_data)は、辞書を展開してキーワード引数として渡します。{"name": "Kim", "email": "...", "gender": "Male"} → User(name="Kim", email="...", gender="Male")。講師メモ — 3限目の表と対比させてください。「画面の関数の中ではVarなのでlenは使えない。ハンドラの中では本物の値なのでlenが使える」。この対比がつかめれば、Reflexの半分は理解できたも同然です。
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,
)name=がすべてをつなぐ鍵です。送信すると、各入力欄のnameがキーになってform_data辞書が作られます。Userのフィールド名(name、email、gender)と完全に一致していないと、User(**form_data)は動きません。on_submit=State.add_userには、ハンドラをクラス経由で、呼び出さずに渡します。フォームが作った辞書が自動的にform_dataに入ります。required=Trueを付けると、送信前にブラウザが空欄をチェックしてくれます。type="button"が重要です。フォーム内のボタンは既定でsubmitになるため、これを忘れるとキャンセルを押しても顧客が追加されてしまいます。rx.flexはdisplay="flex"のボックスです。direction="column"で縦に並び、spacingは間隔です。custom_attrsは、Reflexが知らないHTML属性をそのまま付けたいときに使います。ここではスクリーンリーダー用のラベルです。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(...), # 4-2のフォーム
max_width="450px",
),
)rx.icon("plus")には、Lucideのアイコン名をそのまま使います。add_customer_button()として入れます(タイトルのboxの次、同じ親の中)。ブラウザで確認 — ヘッダーの右端に青い「+ Add User」ボタンが表示されます。
localhost:3000
操作してみようYour customer directory and audience breakdown.
ボタンを押すと背景が暗くなってダイアログが開き、性別は既定値のMaleが選択された状態になっています。上のプレビューで実際に値を入力し、Submitを押してみてください。
ここまでで実行してみると、問題が1つ見つかります。顧客は追加されるのに、ダイアログが閉じないのです。ダイアログの開閉をブラウザが単独で管理しているため、サーバーのハンドラから「閉じて」と伝える手段がないからです。
解決策は、「開いているかどうか」をStateに移すことです。これを制御コンポーネント(controlled component)パターンと呼びます。
class State(rx.State):
users: list[User] = [...]
add_user_dialog_open: bool = False # 1) 開閉状態をステートに持つ
@rx.event
def set_add_user_dialog_open(self, open_: bool): # 2) セッターを自分で定義
self.add_user_dialog_open = open_
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))
self.add_user_dialog_open = False # 3) 送信したら閉じるrx.dialog.root(
...,
open=State.add_user_dialog_open, # ステートが開閉を決める
on_open_change=State.set_add_user_dialog_open, # ユーザーが開閉したらステートに反映
)open=だけを指定してon_open_changeを忘れると、トリガーボタンを押しても状態が変わらず、ダイアログが開きません。この2つは必ずセットで使います。on_open_changeは、開くときにTrue、閉じるとき(Cancel、ESC、外側のクリック)にFalseをハンドラに渡します。State.set_変数名を定義せずに使うコードが多く見られますが、0.10ではそうしたメソッドは自動生成されないため、上のように自分で定義する必要があります。open_にしているのは、Pythonの組み込み関数openと名前がぶつからないようにするための慣習です。reset_on_submit=Falseですが、ダイアログが閉じると中身は画面から取り除かれ、次に開いたときに新しく描画されるので、入力欄は空の状態から始まります。{"name": ..., "email": ..., "gender": ...}にまとめます。add_user(form_data)を実行します。users、add_user_dialog_open)だけがブラウザに返ってきます。ブラウザで確認 — 4-4まで反映してからSubmitを押すと、ダイアログが閉じ、テーブルに3行目が追加されて、バッジが3に変わります。ダイアログが開いたままなら、4-4のopen=とon_open_change=をもう一度確認してください。
localhost:3000
操作してみようYour customer directory and audience breakdown.
"Other"を追加してみましょう。type="email"を付け、不正な形式で送信するとどうなるか確認してみましょう。return rx.toast.error("すでに登録されている顧客です")を返してみましょう。成功したときはrx.toast.success(...)も返してみてください。name=、form_data、Userのフィールドの関係を説明できる。新しい授業・講座のお知らせを受け取る
この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。
R
ReflexKR
触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。
ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する
アフィリエイトと支援について© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
質問と回答
この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。