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
        ),
    )