From be924c750bc66a83cf928ac2110d803501444d63 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 4 Feb 2025 17:56:50 -0800 Subject: [PATCH] improve into component conversion --- reflex/app.py | 4 ++- reflex/compiler/compiler.py | 55 +++++++++++++++++++++++++++++-------- reflex/state.py | 3 ++ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index d9104ece6..27eeb5e68 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -535,7 +535,9 @@ class App(MiddlewareMixin, LifespanMixin): Returns: The generated component. """ - return component if isinstance(component, Component) else component() + from reflex.compiler.compiler import into_component + + return into_component(component) def add_page( self, diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index c2a76aad3..ebc3d5c3b 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -4,7 +4,7 @@ from __future__ import annotations from datetime import datetime from pathlib import Path -from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Tuple, Type, Union from reflex import constants from reflex.compiler import templates, utils @@ -544,7 +544,47 @@ def purge_web_pages_dir(): if TYPE_CHECKING: - from reflex.app import UnevaluatedPage + from reflex.app import ComponentCallable, UnevaluatedPage + + +def _into_component_once(component: Component | ComponentCallable) -> Component | None: + """Convert a component to a Component. + + Args: + component: The component to convert. + + Returns: + The converted component. + """ + if isinstance(component, Component): + return component + if isinstance(component, (Var, int, float, str)): + return Fragment.create(component) + if isinstance(component, Sequence): + return Fragment.create(*component) + return None + + +def into_component(component: Component | ComponentCallable) -> Component: + """Convert a component to a Component. + + Args: + component: The component to convert. + + Returns: + The converted component. + + Raises: + TypeError: If the component is not a Component. + """ + if (converted := _into_component_once(component)) is not None: + return converted + if ( + callable(component) + and (converted := _into_component_once(component())) is not None + ): + return converted + raise TypeError(f"Expected a Component, got {type(component)}") def compile_unevaluated_page( @@ -567,12 +607,7 @@ def compile_unevaluated_page( The compiled component and whether state should be enabled. """ # Generate the component if it is a callable. - component = page.component - component = component if isinstance(component, Component) else component() - - # unpack components that return tuples in an rx.fragment. - if isinstance(component, tuple): - component = Fragment.create(*component) + component = into_component(page.component) component._add_style_recursive(style or {}, theme) @@ -677,10 +712,8 @@ class ExecutorSafeFunctions: The route, compiled component, and compiled page. """ component, enable_state = compile_unevaluated_page( - route, cls.UNCOMPILED_PAGES[route] + route, cls.UNCOMPILED_PAGES[route], cls.STATE, style, theme ) - component = component if isinstance(component, Component) else component() - component._add_style_recursive(style, theme) return route, component, compile_page(route, component, cls.STATE) @classmethod diff --git a/reflex/state.py b/reflex/state.py index 92aaa4710..9b8701d23 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2459,6 +2459,8 @@ class ComponentState(State, mixin=True): Returns: A new instance of the Component with an independent copy of the State. """ + from reflex.compiler.compiler import into_component + cls._per_component_state_instance_count += 1 state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}" component_state = type( @@ -2471,6 +2473,7 @@ class ComponentState(State, mixin=True): setattr(reflex.istate.dynamic, state_cls_name, component_state) component = component_state.get_component(*children, **props) component.State = component_state + component = into_component(component) return component