R

ReflexKR

Reflex 官方文档

用 Reflex 入门 Python 全栈 Web 开发

·

课程概览

1

Reflex 简介与开发环境

2

用组件绘制界面

3

State 与 Var:让数据驱动界面

4

事件与表单:添加客户

5

数据处理与图表

6

总结、调试与下一步

·

附录

用 Reflex 入门 Python 全栈 Web 开发
第4课

时间 2:10–2:55

事件与表单:添加客户

学完这一课,点击“Add User”按钮会打开对话框,提交表单后客户会被添加到表格中,对话框也会自动关闭。这是让界面响应用户操作的第一步,也是最核心的一步。

4-1. 事件处理器(10 分钟)

事件处理器是定义在 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))
  • 事件处理器在服务器上以真正的 Python 运行。在这里 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 就算懂了一半。

4-2. 制作表单(10 分钟)

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 让浏览器在提交前拦住空白输入。
  • Cancel 按钮上的 type="button" 很重要。表单中的按钮默认是 submit,如果漏写了,点“取消”也会添加客户。
  • rx.flex 是 display="flex" 的盒子。direction="column" 表示纵向排列,spacing 是间距。
  • custom_attrs 用于直接附加 Reflex 不认识的 HTML 属性。这里加的是给屏幕阅读器用的标签。

4-3. 用对话框包起来(10 分钟)

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 之后,同一个父元素内)。
  • 把界面片段拆分成函数,和普通 Python 函数拆分没有区别,不需要任何特殊语法。

在浏览器中确认 — 页头最右侧出现一个蓝色的“+ Add User”按钮。

localhost:3000

动手试试

Customers

2

Your customer directory and audience breakdown.

NameEmailGender
Danilo Sousadanilo@example.comMale
Zahra Ambessazahra@example.comFemale
4-3 步:点击 Add User 添加客户试试。客户能添加成功,但对话框不会关闭。

点击按钮后,背景变暗并弹出对话框,性别默认选中 Male。可以在上面的预览中亲自输入内容,然后点击 Submit 试试。

做到这里运行一下,会发现一个问题:客户添加成功了,但对话框没有关闭。这是因为对话框的打开/关闭状态完全由浏览器自己管理,服务器上的事件处理器没有办法通知它关闭。

4-4. 用状态控制对话框(10 分钟)

解决办法是把“是否打开”这件事交给 State 管理。这种做法叫做受控组件(controlled component)模式。

class State(rx.State):
    users: list[User] = [...]
    add_user_dialog_open: bool = False          # 1) 把打开状态放进 State

    @rx.event
    def set_add_user_dialog_open(self, open_: bool):   # 2) 自己定义 setter
        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,点击触发按钮时状态不会变化,对话框就打不开。这两者必须成对出现。
  • on_open_change 会在打开时把 True、关闭时(Cancel、ESC、点击外部)把 False 传给事件处理器。
  • Reflex 0.10 没有自动生成的 setter。很多旧资料里会直接使用未定义的 State.set_变量名,但 0.10 不会自动生成这类方法,所以要像上面那样自己定义。
  • 参数名用 open_,是为了避免和 Python 内置函数 open 重名的惯例写法。
  • 虽然设置了 reset_on_submit=False,但对话框关闭后其内容会从页面上移除,再次打开时重新渲染,所以输入框会恢复为空。

4-5. 一次点击经历的全过程(5 分钟)

  1. 用户点击 Submit。
  2. 浏览器把输入值收集成 {"name": ..., "email": ..., "gender": ...}。
  3. 这些值通过 WebSocket 发送到服务器。
  4. 服务器找到该用户的 State,执行 add_user(form_data)。
  5. 只有发生变化的变量(users、add_user_dialog_open)会被发回浏览器。
  6. 表格和徽章重新渲染,对话框关闭。

在浏览器中确认 — 应用完 4-4 的修改后点击 Submit,对话框会关闭,表格新增第 3 行,徽章变为 3。如果对话框仍然开着,请再检查一下 4-4 中的 open= 和 on_open_change=。

localhost:3000

动手试试

Customers

2

Your customer directory and audience breakdown.

NameEmailGender
Danilo Sousadanilo@example.comMale
Zahra Ambessazahra@example.comFemale
第4课完成:提交后对话框关闭,徽章数字增加。

动手练习

  1. 在性别选项中加入 "Other"。
  2. 给邮箱输入框加上 type="email",看看提交格式错误的内容时会发生什么。
  3. 在事件处理器中,如果姓名重复就不添加,而是返回 return rx.toast.error("该客户已存在")。添加成功时也试着返回 rx.toast.success(...)。

检查点

  • 用 Add User 添加客户后,表格和徽章会随之变化。
  • 提交后对话框会自动关闭。
  • 点击 Cancel 不会添加客户。
  • 能解释 name=、form_data 与 User 字段之间的关系。

← 上一课

第3课 · State 与 Var:让数据驱动界面

下一课 →

第5课 · 数据处理与图表

订阅新课时和新课程通知

本课程新增课时或推出后续课程时,我们会发邮件通知你。

收集项目:电子邮箱 · 用途:本课程的新课时和上线通知 · 保存期限:直到你要求删除。不会用于其他用途,也不会提供给他人。回复任意通知邮件,我们会立即删除。

问答

关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。

以纯文本显示,不支持链接和格式。

R

ReflexKR

动手学 Python 和 Reflex。所有示例均已在 Reflex 0.10.0 上实际运行验证。

ReflexKR 免费,没有横幅广告。如果对你有帮助,欢迎请我们喝杯咖啡。 ☕ 请我们喝杯咖啡

联盟链接与赞助说明
한국어English日本語简体中文EspañolDeutsch

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

Built with Reflex