Przeglądaj źródła

[REF-2158] Enable state when `on_load` or event_triggers are set. (#2815)

* [REF-2158] Enable state when `on_load` or event_triggers are set.

Basically all events in a reflex app require the backend to be up, even the
built-in server side events round trip to the backend and require "state" even
if no user-defined state classes are declared.

test_app_state_determination: checking that state is enabled at the right time

* Clear out DECORATED_PAGES when initializing a new app
Masen Furer 1 rok temu
rodzic
commit
c809107d09
4 zmienionych plików z 61 dodań i 6 usunięć
  1. 10 6
      reflex/app.py
  2. 14 0
      reflex/components/component.py
  3. 2 0
      reflex/testing.py
  4. 35 0
      tests/test_app.py

+ 10 - 6
reflex/app.py

@@ -453,14 +453,18 @@ class App(Base):
         # Generate the component if it is a callable.
         component = self._generate_component(component)
 
+        # Ensure state is enabled if this page uses state.
         if self.state is None:
-            for var in component._get_vars(include_children=True):
-                if not var._var_data:
-                    continue
-                if not var._var_data.state:
-                    continue
+            if on_load or component._has_event_triggers():
                 self.enable_state()
-                break
+            else:
+                for var in component._get_vars(include_children=True):
+                    if not var._var_data:
+                        continue
+                    if not var._var_data.state:
+                        continue
+                    self.enable_state()
+                    break
 
         component = OverlayFragment.create(component)
 

+ 14 - 0
reflex/components/component.py

@@ -873,6 +873,20 @@ class Component(BaseComponent, ABC):
 
         return vars
 
+    def _has_event_triggers(self) -> bool:
+        """Check if the component or children have any event triggers.
+
+        Returns:
+            True if the component or children have any event triggers.
+        """
+        if self.event_triggers:
+            return True
+        else:
+            for child in self.children:
+                if isinstance(child, Component) and child._has_event_triggers():
+                    return True
+        return False
+
     def _get_custom_code(self) -> str | None:
         """Get custom code for the component.
 

+ 2 - 0
reflex/testing.py

@@ -225,6 +225,8 @@ class AppHarness:
         with chdir(self.app_path):
             # ensure config and app are reloaded when testing different app
             reflex.config.get_config(reload=True)
+            # Clean out any `rx.page` decorators from other tests.
+            reflex.app.DECORATED_PAGES.clear()
             # reset rx.State subclasses
             State.class_subclasses.clear()
             State.class_subclasses.update(INTERNAL_STATES)

+ 35 - 0
tests/test_app.py

@@ -1319,3 +1319,38 @@ def test_app_wrap_priority(compilable_app):
         ")"
         "}"
     ) in "".join(app_js_lines)
+
+
+def test_app_state_determination():
+    """Test that the stateless status of an app is determined correctly."""
+    a1 = App()
+    assert a1.state is None
+
+    # No state, no router, no event handlers.
+    a1.add_page(rx.box("Index"), route="/")
+    assert a1.state is None
+
+    # Add a page with `on_load` enables state.
+    a1.add_page(rx.box("About"), route="/about", on_load=rx.console_log(""))
+    assert a1.state is not None
+
+    a2 = App()
+    assert a2.state is None
+
+    # Referencing a state Var enables state.
+    a2.add_page(rx.box(rx.text(GenState.value)), route="/")
+    assert a2.state is not None
+
+    a3 = App()
+    assert a3.state is None
+
+    # Referencing router enables state.
+    a3.add_page(rx.box(rx.text(State.router.page.full_path)), route="/")
+    assert a3.state is not None
+
+    a4 = App()
+    assert a4.state is None
+
+    # Referencing an event handler enables state.
+    a4.add_page(rx.box(rx.button("Click", on_click=rx.console_log(""))), route="/")
+    assert a4.state is not None