Vars
Base Vars
变量是 app 中随时间可能发生变化的任何字段。变量直接呈现在应用程序的前端。
Base vars are defined as fields in your State class.
它们可以具有预设的默认值。如果您不提供默认值,则必须提供类型注释
class TickerState(rx.State):
ticker: str = "AAPL"
price: str = "$150"
def ticker_example():
return rx.center(
rx.vstack(
rx.heading(TickerState.ticker, size="3"),
rx.text(
f"Current Price: {TickerState.price}",
font_size="md",
),
rx.text("Change: 4%", color="green"),
),
)
Vars must be JSON serializable.
Accessing state variables on different pages
访问不同页面上的状态变量
状态只是一个 Python 类,因此可以在一页上定义,然后在另一页上导入并使用。在下面我们在页面 state.py
上定义 TickerState
类,然后在页面 index.py
上导入并使用它。
# state.py
class TickerState(rx.State):
ticker: str = "AAPL"
price: str = "$150"
# index.py
from .state import TickerState
def ticker_example():
return rx.center(
rx.vstack(
rx.heading(TickerState.ticker, size="3"),
rx.text(
f"Current Price: reflex___state____state__flexdown___modules____a4e3de79f19b87b4f4fce3d5f729c6b6b225d8d968533932d2e49adf411f2694____ticker_state.price",
font_size="md",
),
rx.text("Change: 4%", color="green"),
),
)
Backend-only Vars
任何以下划线开头的状态类中的变量都被视为仅后端使用,不会与前端同步。与特定 session 相关的数据,如果不直接呈现在前端,则应存储在仅后端变量中,以减少网络流量并提高性能。
他们的优势在于它们不需要是 JSON 可序列化的,但是它们仍然必须是 cloudpickle-able,以便在生产模式下与 redis 一起使用。它们不能直接在前端渲染,并且可能用于存储不应发送给客户端的敏感值。
例如,仅后端变量用于存储一个大数据结构,然后使用缓存变量将其分页到前端。
import numpy as np
class BackendVarState(rx.State):
_backend: np.ndarray = np.array(
[random.randint(0, 100) for _ in range(100)]
)
offset: int = 0
limit: int = 10
@rx.var(cache=True)
def page(self) -> list[int]:
return [
int(x) # explicit cast to int
for x in self._backend[
self.offset : self.offset + self.limit
]
]
@rx.var(cache=True)
def page_number(self) -> int:
return (
(self.offset // self.limit)
+ 1
+ (1 if self.offset % self.limit else 0)
)
@rx.var(cache=True)
def total_pages(self) -> int:
return len(self._backend) // self.limit + (
1 if len(self._backend) % self.limit else 0
)
@rx.event
def prev_page(self):
self.offset = max(self.offset - self.limit, 0)
@rx.event
def next_page(self):
if self.offset + self.limit < len(self._backend):
self.offset += self.limit
@rx.event
def generate_more(self):
self._backend = np.append(
self._backend,
[
random.randint(0, 100)
for _ in range(random.randint(0, 100))
],
)
@rx.event
def set_limit(self, value: str):
self.limit = int(value)
def backend_var_example():
return rx.vstack(
rx.hstack(
rx.button(
"Prev",
on_click=BackendVarState.prev_page,
),
rx.text(
f"Page {BackendVarState.page_number} / {BackendVarState.total_pages}"
),
rx.button(
"Next",
on_click=BackendVarState.next_page,
),
rx.text("Page Size"),
rx.input(
width="5em",
value=BackendVarState.limit,
on_change=BackendVarState.set_limit,
),
rx.button(
"Generate More",
on_click=BackendVarState.generate_more,
),
),
rx.list(
rx.foreach(
BackendVarState.page,
lambda x, ix: rx.text(
f"_backend[{ix + BackendVarState.offset}] = {x}"
),
),
),
)
Using rx.field / rx.Field to improve type hinting for vars
When defining state variables you can use rx.Field[T]
to annotate the variable's type. Then, you can initialize the variable using rx.field(default_value)
, where default_value
is an instance of type T
.
这种方法使变量的类型明确,有助于静态分析工具进行类型检查。此外,它会显示您的前端代码中允许修改变量的方法,因为它们在类型提示中列出。
import reflex as rx
app = rx.App()
class State(rx.State):
x: rx.Field[bool] = rx.field(False)
def flip(self):
self.x = not self.x
@app.add_page
def index():
return rx.vstack(
rx.button("Click me", on_click=State.flip),
rx.text(State.x),
rx.text(~State.x),
)
Computed Vars
Computed vars have values derived from other properties on the backend. They are defined as methods in your State class with the @rx.var
decorator. A computed var is recomputed every time an event is processed in your app.
class UppercaseState(rx.State):
text: str = "hello"
@rx.var
def upper_text(self) -> str:
# This will be recomputed whenever `text` changes.
return self.text.upper()
def uppercase_example():
return rx.vstack(
rx.heading(UppercaseState.upper_text),
rx.input(
on_blur=UppercaseState.set_text,
placeholder="Type here...",
),
)
We recommend always using type annotations for computed vars.
Cached Vars 缓存变量
装饰为 @rx.var(cache=True)
的缓存变量是一种特殊类型的计算变量,仅在其依赖的其他状态变量更改时重新计算。这对于昂贵的计算很有用,但在某些情况下,它可能不会在您期望的时候更新。 Reflex 的早期版本具有 @rx.cached_var
装饰器,现在已被 @rx.var
的新 cache
参数取代。
class CachedVarState(rx.State):
counter_a: int = 0
counter_b: int = 0
@rx.var
def last_touch_time(self) -> str:
# This is updated anytime the state is updated.
return time.strftime("%H:%M:%S")
@rx.event
def increment_a(self):
self.counter_a += 1
@rx.var(cache=True)
def last_counter_a_update(self) -> str:
# This is updated only when `counter_a` changes.
return f"{self.counter_a} at {time.strftime('%H:%M:%S')}"
@rx.event
def increment_b(self):
self.counter_b += 1
@rx.var(cache=True)
def last_counter_b_update(self) -> str:
# This is updated only when `counter_b` changes.
return f"{self.counter_b} at {time.strftime('%H:%M:%S')}"
def cached_var_example():
return rx.vstack(
rx.text(
f"State touched at: {CachedVarState.last_touch_time}"
),
rx.text(
f"Counter A: {CachedVarState.last_counter_a_update}"
),
rx.text(
f"Counter B: {CachedVarState.last_counter_b_update}"
),
rx.hstack(
rx.button(
"Increment A",
on_click=CachedVarState.increment_a,
),
rx.button(
"Increment B",
on_click=CachedVarState.increment_b,
),
),
)
在这个例子中 last_touch_time
是一个普通的计算变量,它会在状态被修改时更新。 last_counter_a_update
是一个仅依赖于 counter_a
的计算变量,因此只有在 counter_a
发生变化时才会重新计算。类似地, last_counter_b_update
仅依赖于 counter_b
,因此只有在 counter_b
发生变化时才会更新。
Var Operations 变量操作
Var operations transform the placeholder representation of the value on the frontend and provide a way to perform basic operations on the Var without having to define a computed var.
在您的前端组件中,您不能在状态变量上使用任意的 Python 函数;This is because we compile the frontend to Javascript. 这是就要用到一些变量操作了
Supported Operations
- Negate, Absolute and Length
-
运算符用于获取变量的负值。abs()
运算符用于获取变量的绝对值。.length()
运算符用于获取列表变量的长度。
- 所有的比较运算符在 Python 中都如预期那样使用。包括
==
,!=
,>
,>=
,<
,<=
- 两个变量相加
+
,相减-
,相乘*
和将一个变量提升到幂pow()
- 运算符
/
表示真除法。运算符//
表示地板除法。运算符%
表示除法的余数 &
表示逻辑 AND。|
表示逻辑 OR。~
运算符用于反转一个变量。它用于类型为bool
的变量,并等同于not
运算符。- Var 类型不支持''in''运算符,必须使用
Var.contains()
。此时 var 必须是类型:dict
,list
,tuple
或str
- 使用
reverse
操作来反转列表 var。var 必须是类型list
- 使用
join
操作将列表 var 连接成字符串 lower
运算符将字符串变量转换为小写。upper
运算符将字符串变量转换为大写。split
运算符将字符串变量拆分为列表。
Get Item (Indexing) 获取项目(索引)
索引仅支持字符串、列表、元组、字典和数据框。要索引到状态变量,需要严格的类型注释。
class GetItemState1(rx.State):
# list_1: list = [50, 10, 20] will not work
list_1: list[int] = [50, 10, 20]
def get_item_error_1():
return rx.vstack(
rx.progress(value=GetItemState1.list_1[0])
)
Custom Vars
如前所述,Reflex vars 必须是 JSON 可序列化的。
这意味着我们可以支持任何 Python 原始类型,以及列表、字典和元组。但是,您还可以通过继承 rx.Base
或使用 @dataclasses.dataclass
将它们装饰为数据类来创建更复杂的 var 类型。
import googletrans
class Translation(rx.Base):
original_text: str
translated_text: str
class TranslationState(rx.State):
input_text: str = "Hola Mundo"
current_translation: Translation = Translation(
original_text="", translated_text=""
)
@rx.event
def translate(self):
self.current_translation.original_text = (
self.input_text
)
self.current_translation.translated_text = (
googletrans.Translator()
.translate(self.input_text, dest="en")
.text
)
def translation_example():
return rx.vstack(
rx.input(
on_blur=TranslationState.setvar("input_text"),
default_value=TranslationState.input_text,
placeholder="Text to translate...",
),
rx.button(
"Translate", on_click=TranslationState.translate
),
rx.text(
TranslationState.current_translation.translated_text
),
)