第2课用 rx.cond 代替了 if,第3课用 rx.foreach 代替了 for。明明写的是 Python,为什么不能直接用 Python 语法?这一课把原因讲透,你就能一下子明白在 Reflex 中遇到的大多数错误是怎么来的。
Reflex 应用的代码会在两个不同的时间点执行。
| 编译时 | 运行时 | |
|---|---|---|
| 何时 | 应用启动时执行一次 | 应用运行期间持续执行 |
| 执行什么 | 界面函数(show_login、render_item …) | 事件处理器(increment、toggle_login …) |
| 在哪里 | Python 代码被转换为 JavaScript(React)代码 | 服务器上的 Python |
| State 的值 | 还不知道 | 有真实的值 |
界面函数在应用启动时被翻译成 JavaScript,再发送到浏览器。翻译的时候,还不知道哪个用户会点什么,所以界面函数里的 LoginState.logged_in 并不是真正的 True/False,而是一个"之后在浏览器里才会填入的占位"。这个占位就是 Var。
反过来,事件处理器不会被翻译,而是以 Python 原样在服务器上执行。所以在处理器里,任何 Python 代码和库都可以使用。
下面的处理器既用了 if,又调用了定义在 State 外部的函数。因为它是在运行时于服务器上执行的,所以完全没问题。
def check_even(num: int):
return num % 2 == 0
class MyState3(rx.State):
count: int = 0
text: str = "even"
@rx.event
def increment(self):
# Use any Python code within state.
# Even reference functions defined outside the state.
if check_even(self.count):
self.text = "even"
else:
self.text = "odd"
self.count += 1
def count_and_check():
return rx.box(
rx.heading(MyState3.text, as_="h2"),
rx.button("Increment", on_click=MyState3.increment),
)localhost:3000
动手试试界面函数里也不是不能写 Python,但只能用于编译时就已经确定值的东西(不涉及 State var 的部分)。下面的 range(5) 和 check_even(i) 在应用启动时就已经计算完毕。
def show_numbers():
return rx.vstack(*[rx.text(f"{i} is {'even' if check_even(i) else 'odd'}") for i in range(5)])localhost:3000
运行效果0 is even
1 is odd
2 is even
3 is odd
4 is even
State var 在编译时还没有值,所以在界面函数中写下面这些代码都会报错。
用 if 判断 Var 的值:还不知道它是真是假。用来选择 props 的值时也一样。
class BadState(rx.State):
count: int = 0
def count_if_even():
return rx.box(
rx.heading("Count: ", as_="h2"),
# This will raise a compile error, as BadState.count is a var and not known at compile time.
rx.text(BadState.count if BadState.count % 2 == 0 else "Odd"),
# Using an if statement with a var as a prop will NOT work either.
rx.text("hello", color="red" if BadState.count % 2 == 0 else "blue"),
)用 for 遍历列表 Var:还不知道里面有几个元素。
class BadState(rx.State):
items: list[str] = ["Apple", "Banana", "Cherry"]
def loop_over_list():
return rx.box(
# This will raise a compile error, as BadState.items is a list and not known at compile time.
*[rx.text(item) for item in BadState.items]
)把 Var 传给 len() 这类 Python 函数:要统计字数的字符串还不存在。
class BadTextState(rx.State):
text: str = "Hello world"
def format_text():
return rx.box(
# Python operations such as `len` will not work on state vars.
rx.text(len(BadTextState.text)),
)那么,要在界面上显示"偶数显示 Even,奇数显示 Odd"该怎么办?Reflex 准备了可用于 Var 的 Var 运算。对 Var 使用 %、==、> 等运算符,得到的结果也是 Var,真正的计算要等到值传进浏览器之后才进行。
class CountEvenState(rx.State):
count: int = 0
@rx.event
def increment(self):
self.count += 1
def count_if_even():
return rx.box(
rx.heading("Count: ", CountEvenState.count, as_="h2"),
rx.cond(
# Here we use the `%` and `==` var operations to check if the count is even.
CountEvenState.count % 2 == 0,
rx.text("Even"),
rx.text("Odd"),
),
rx.button("Increment", on_click=CountEvenState.increment),
)localhost:3000
动手试试Even
现在可以用一张表总结第1~3课用到的工具为什么必不可少。
| 想做的事 | 界面代码中不能用 | 改用 |
|---|---|---|
| 根据条件显示不同界面 | if / else | rx.cond(条件, 为真时, 为假时) |
| 遍历列表 | for、列表推导式 | rx.foreach(列表, 函数) |
| 求长度 | len(x) | x.length() |
| 计算、比较 | 把 Var 传给 Python 函数 | %、==、> 等 Var 运算 |
| 复杂计算 | 界面函数里的 Python | 在事件处理器中计算后存入 State |
小贴士 — 拿不准的时候,问自己一句:"这段代码是什么时候执行的?"如果在界面函数里,那就是编译时,Var 还只是个占位;如果在处理器里,那就是运行时,拿到的是真实的值。
format_text,让它显示字符数。提示:BadTextState.text.length()。count_if_even 中,当 count 大于等于 10 时显示 "Big!"。提示:rx.cond(CountEvenState.count >= 10, ...)。MyState3 那样在处理器里计算的方式,和像 CountEvenState 那样使用 Var 运算的方式。哪一种需要的 State 变量更少?if、for、len()。rx.cond、rx.foreach 实现同样的效果。订阅新课时和新课程通知
本课程新增课时或推出后续课程时,我们会发邮件通知你。
问答
关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。