Browse Source

implement rx dynamic (#4195)

* implement rx dynamic

* dang it darglint

* add custom type
Khaleel Al-Adhami 6 tháng trước cách đây
mục cha
commit
3eab2b6e7d
4 tập tin đã thay đổi với 53 bổ sung0 xóa
  1. 1 0
      reflex/__init__.py
  2. 1 0
      reflex/__init__.pyi
  3. 47 0
      reflex/state.py
  4. 4 0
      reflex/utils/exceptions.py

+ 1 - 0
reflex/__init__.py

@@ -331,6 +331,7 @@ _MAPPING: dict = {
         "var",
         "ComponentState",
         "State",
+        "dynamic",
     ],
     "style": ["Style", "toggle_color_mode"],
     "utils.imports": ["ImportVar"],

+ 1 - 0
reflex/__init__.pyi

@@ -184,6 +184,7 @@ from .model import session as session
 from .page import page as page
 from .state import ComponentState as ComponentState
 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

+ 47 - 0
reflex/state.py

@@ -30,6 +30,7 @@ from typing import (
     Set,
     Tuple,
     Type,
+    TypeVar,
     Union,
     cast,
     get_args,
@@ -79,6 +80,7 @@ from reflex.utils import console, format, path_ops, prerequisites, types
 from reflex.utils.exceptions import (
     ComputedVarShadowsBaseVars,
     ComputedVarShadowsStateVar,
+    DynamicComponentInvalidSignature,
     DynamicRouteArgShadowsStateVar,
     EventHandlerShadowsBuiltInStateMethod,
     ImmutableStateError,
@@ -2095,6 +2097,51 @@ 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.
+
+    Raises:
+        DynamicComponentInvalidSignature: If the function does not have exactly one parameter.
+        DynamicComponentInvalidSignature: If the function does not have a type hint for the state class.
+    """
+    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 DynamicComponentInvalidSignature(
+            "The function must have exactly one parameter, which is the state class."
+        )
+
+    if len(values) != 1:
+        raise DynamicComponentInvalidSignature(
+            "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."""
 

+ 4 - 0
reflex/utils/exceptions.py

@@ -139,3 +139,7 @@ class StateSchemaMismatchError(ReflexError, TypeError):
 
 class EnvironmentVarValueError(ReflexError, ValueError):
     """Raised when an environment variable is set to an invalid value."""
+
+
+class DynamicComponentInvalidSignature(ReflexError, TypeError):
+    """Raised when a dynamic component has an invalid signature."""