diff --git a/reflex/__init__.py b/reflex/__init__.py index ad51d2cf4..8ae54f476 100644 --- a/reflex/__init__.py +++ b/reflex/__init__.py @@ -329,6 +329,7 @@ _MAPPING: dict = { "SessionStorage", "ComponentState", "State", + "dynamic", ], "style": ["Style", "toggle_color_mode"], "utils.imports": ["ImportVar"], diff --git a/reflex/__init__.pyi b/reflex/__init__.pyi index d928778d8..2e8728027 100644 --- a/reflex/__init__.pyi +++ b/reflex/__init__.pyi @@ -184,6 +184,7 @@ from .state import Cookie as Cookie from .state import LocalStorage as LocalStorage from .state import SessionStorage as SessionStorage from .state import State as State +from .state import dynamic as dynamic from .state import var as var from .style import Style as Style from .style import toggle_color_mode as toggle_color_mode diff --git a/reflex/state.py b/reflex/state.py index 0d6eed0ed..5eeed36cc 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -31,6 +31,7 @@ from typing import ( Set, Tuple, Type, + TypeVar, Union, cast, get_args, @@ -2091,6 +2092,47 @@ class State(BaseState): is_hydrated: bool = False +T = TypeVar("T", bound=BaseState) + + +def dynamic(func: Callable[[T], Component]): + """Create a dynamically generated components from a state class. + + Args: + func: The function to generate the component. + + Returns: + The dynamically generated component. + """ + number_of_parameters = len(inspect.signature(func).parameters) + + func_signature = get_type_hints(func) + + if "return" in func_signature: + func_signature.pop("return") + + values = list(func_signature.values()) + + if number_of_parameters != 1: + raise ValueError( + "The function must have exactly one parameter, which is the state class." + ) + + if len(values) != 1: + raise ValueError( + "You must provide a type hint for the state class in the function." + ) + + state_class: Type[T] = values[0] + + def wrapper() -> Component: + from reflex.components.base.fragment import fragment + + return fragment(state_class._evaluate(lambda state: func(state))) + + return wrapper + + class FrontendEventExceptionState(State): """Substate for handling frontend exceptions."""