From 056fd648c1b6bb04f9e847f738f2d6a375b80d38 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 21:24:50 +0000 Subject: [PATCH 1/2] Fix ReferenceError when debounce_input wraps native rx.el elements DebounceInput.create passed the child's tag as a bare JS identifier in the element prop, so wrapping a native DOM element like rx.el.input compiled to element:input and crashed the page with "ReferenceError: input is not defined". Component._render already quotes global-scope tags; extract that logic into Component._get_tag_name() and use it for the element prop so native elements render as element:"input". https://claude.ai/code/session_01DDtiK8EdqxtjVFRuykh4hD --- .../src/reflex_base/components/component.py | 19 ++- .../reflex_components_core/core/debounce.py | 2 +- .../tests_playwright/test_debounce_input.py | 111 ++++++++++++++++++ tests/units/components/core/test_debounce.py | 15 +++ 4 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 tests/integration/tests_playwright/test_debounce_input.py diff --git a/packages/reflex-base/src/reflex_base/components/component.py b/packages/reflex-base/src/reflex_base/components/component.py index 33e597c966c..441de7b08b5 100644 --- a/packages/reflex-base/src/reflex_base/components/component.py +++ b/packages/reflex-base/src/reflex_base/components/component.py @@ -1172,6 +1172,18 @@ def _exclude_props(self) -> list[str]: """ return [] + def _get_tag_name(self) -> str: + """Get the JS expression used to reference this component's tag. + + Returns: + The alias (or tag) identifier, quoted as a string literal when the + tag is a global scope element like ``"input"``. + """ + name = (self.tag if not self.alias else self.alias) or "" + if self._is_tag_in_global_scope and self.library is None: + name = '"' + name + '"' + return name + def _render(self, props: dict[str, Any] | None = None) -> Tag: """Define how to render the component in React. @@ -1181,14 +1193,9 @@ def _render(self, props: dict[str, Any] | None = None) -> Tag: Returns: The tag to render. """ - # Create the base tag. - name = (self.tag if not self.alias else self.alias) or "" - if self._is_tag_in_global_scope and self.library is None: - name = '"' + name + '"' - # Create the base tag. tag = Tag( - name=name, + name=self._get_tag_name(), special_props=self.special_props.copy(), ) diff --git a/packages/reflex-components-core/src/reflex_components_core/core/debounce.py b/packages/reflex-components-core/src/reflex_components_core/core/debounce.py index b627e87eea5..6b74877e4be 100644 --- a/packages/reflex-components-core/src/reflex_components_core/core/debounce.py +++ b/packages/reflex-components-core/src/reflex_components_core/core/debounce.py @@ -132,7 +132,7 @@ def create(cls, *children: Component, **props: Any) -> Component: props.setdefault( "element", Var( - _js_expr=str(child.alias or child.tag), + _js_expr=child._get_tag_name(), _var_type=type[Component], _var_data=VarData( imports=child._get_imports(), diff --git a/tests/integration/tests_playwright/test_debounce_input.py b/tests/integration/tests_playwright/test_debounce_input.py new file mode 100644 index 00000000000..961d6f7ac9d --- /dev/null +++ b/tests/integration/tests_playwright/test_debounce_input.py @@ -0,0 +1,111 @@ +"""Integration test for ``rx.debounce_input`` wrapping native ``rx.el`` elements. + +Regression coverage for the frontend ``ReferenceError: input is not defined`` +that fired when DebounceInput passed a native DOM tag (e.g. ``rx.el.input``) +as a bare identifier in the ``element`` prop instead of a string literal. +""" + +from collections.abc import Generator + +import pytest +from playwright.sync_api import Page, expect + +from reflex.testing import AppHarness + + +def DebounceNativeElementApp(): + """App wrapping native DOM elements in rx.debounce_input.""" + import reflex as rx + + class State(rx.State): + text: str = "" + area: str = "" + + @rx.event + def set_text(self, value: str): + self.text = value + + @rx.event + def set_area(self, value: str): + self.area = value + + @rx.page("/") + def index(): + return rx.box( + rx.input( + value=State.router.session.client_token, + read_only=True, + id="token", + ), + rx.debounce_input( + rx.el.input( + placeholder="Search...", + on_change=State.set_text, + id="native-input", + ), + debounce_timeout=50, + ), + rx.debounce_input( + rx.el.textarea( + on_change=State.set_area, + id="native-textarea", + ), + debounce_timeout=50, + ), + rx.text(State.text, id="text-value"), + rx.text(State.area, id="area-value"), + ) + + app = rx.App() # noqa: F841 + + +@pytest.fixture(scope="module") +def debounce_native_element_app( + tmp_path_factory, +) -> Generator[AppHarness, None, None]: + """Start DebounceNativeElementApp via AppHarness. + + Args: + tmp_path_factory: pytest fixture for creating temporary directories. + + Yields: + Running AppHarness instance. + """ + with AppHarness.create( + root=tmp_path_factory.mktemp("debounce_native_element_app"), + app_source=DebounceNativeElementApp, + ) as harness: + assert harness.app_instance is not None, "app is not running" + yield harness + + +def test_debounce_wrapped_native_elements( + debounce_native_element_app: AppHarness, page: Page +): + """Debounced native input/textarea render and propagate changes to state. + + Args: + debounce_native_element_app: AppHarness running the test app. + page: Playwright page. + """ + assert debounce_native_element_app.frontend_url is not None + + page_errors: list[str] = [] + page.on("pageerror", lambda exc: page_errors.append(str(exc))) + + page.goto(debounce_native_element_app.frontend_url) + expect(page.locator("#token")).not_to_have_value("") + + # DebounceInput must render the actual native tags with carried props. + native_input = page.locator("input#native-input") + expect(native_input).to_have_attribute("placeholder", "Search...") + native_textarea = page.locator("textarea#native-textarea") + expect(native_textarea).to_be_visible() + + native_input.fill("hello") + expect(page.locator("#text-value")).to_have_text("hello") + + native_textarea.fill("world") + expect(page.locator("#area-value")).to_have_text("world") + + assert not page_errors, f"Frontend raised unexpected errors: {page_errors}" diff --git a/tests/units/components/core/test_debounce.py b/tests/units/components/core/test_debounce.py index ccb3fdf3265..51a6377a97a 100644 --- a/tests/units/components/core/test_debounce.py +++ b/tests/units/components/core/test_debounce.py @@ -112,6 +112,21 @@ def test_render_with_special_props(): assert next(iter(tag.special_props)).equals(special_prop) +def test_render_native_element_child(): + """DebounceInput quotes the element prop for native DOM element children.""" + tag = rx.debounce_input(rx.el.input(on_change=S.on_change))._render() + assert tag.props["element"]._js_expr == '"input"' + + tag = rx.debounce_input(rx.el.textarea(on_change=S.on_change))._render() + assert tag.props["element"]._js_expr == '"textarea"' + + +def test_render_library_element_child(): + """DebounceInput references a library component child by its identifier.""" + tag = rx.debounce_input(rx.input(on_change=S.on_change))._render() + assert tag.props["element"]._js_expr == "RadixThemesTextField.Root" + + def test_event_triggers(): debounced_input = rx.debounce_input( rx.input( From 69d7aa1089931bdf2becfdb05bb8a3bfac283500 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 21:41:43 +0000 Subject: [PATCH 2/2] Add news fragments for #6637 https://claude.ai/code/session_01DDtiK8EdqxtjVFRuykh4hD --- packages/reflex-base/news/6637.misc.md | 1 + packages/reflex-components-core/news/6637.bugfix.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/reflex-base/news/6637.misc.md create mode 100644 packages/reflex-components-core/news/6637.bugfix.md diff --git a/packages/reflex-base/news/6637.misc.md b/packages/reflex-base/news/6637.misc.md new file mode 100644 index 00000000000..c2d1a8b773c --- /dev/null +++ b/packages/reflex-base/news/6637.misc.md @@ -0,0 +1 @@ +`Component` gained a private `_get_tag_name()` helper returning the JS expression that references the component's tag (quoted for global-scope DOM tags without a library); `Component._render` and `DebounceInput` now share it instead of duplicating the quoting logic. diff --git a/packages/reflex-components-core/news/6637.bugfix.md b/packages/reflex-components-core/news/6637.bugfix.md new file mode 100644 index 00000000000..28902eb2dca --- /dev/null +++ b/packages/reflex-components-core/news/6637.bugfix.md @@ -0,0 +1 @@ +`rx.debounce_input` no longer crashes the page with `ReferenceError: input is not defined` when wrapping a native DOM element such as `rx.el.input` or `rx.el.textarea`. The `element` prop now passes global-scope tags as string literals (`element:"input"`), while library components keep referencing their imported identifiers.