1
0
Эх сурвалжийг харах

Merge branch 'main' into lendemor/builtins_states

Lendemor 7 сар өмнө
parent
commit
7320425285

+ 4 - 1
reflex/__init__.py

@@ -328,7 +328,10 @@ _MAPPING: dict = {
     ],
     "middleware": ["middleware", "Middleware"],
     "model": ["session", "Model"],
-    "state": ["var"],
+    "state": [
+        "var",
+        "dynamic",
+    ],
     "style": ["Style", "toggle_color_mode"],
     "utils.imports": ["ImportVar"],
     "utils.serializers": ["serializer"],

+ 1 - 0
reflex/__init__.pyi

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

+ 50 - 0
reflex/state.py

@@ -30,6 +30,7 @@ from typing import (
     Set,
     Tuple,
     Type,
+    TypeVar,
     Union,
     cast,
     get_args,
@@ -78,6 +79,7 @@ from reflex.utils import console, format, path_ops, prerequisites, types
 from reflex.utils.exceptions import (
     ComputedVarShadowsBaseVars,
     ComputedVarShadowsStateVar,
+    DynamicComponentInvalidSignature,
     DynamicRouteArgShadowsStateVar,
     EventHandlerShadowsBuiltInStateMethod,
     ImmutableStateError,
@@ -94,6 +96,9 @@ from reflex.vars import VarData
 Delta = Dict[str, Any]
 var = computed_var
 
+if TYPE_CHECKING:
+    from reflex.components.component import Component
+
 
 # If the state is this large, it's considered a performance issue.
 TOO_LARGE_SERIALIZED_STATE = 100 * 1024  # 100kb
@@ -2085,6 +2090,51 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
         return state
 
 
+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 StateProxy(wrapt.ObjectProxy):
     """Proxy of a state instance to control mutability of vars for a background task.
 

+ 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."""