From edf223fd155429ea5c1e44b59cdd5fb86b6addaf Mon Sep 17 00:00:00 2001 From: Prachi-Gupta2808 Date: Thu, 2 Jul 2026 21:10:28 +0530 Subject: [PATCH] Fix PyScript root component not hiding after render error --- .../executors/pyscript/layout_handler.py | 2 ++ .../pyscript_components/root_error.py | 20 ++++++++++++++ tests/test_pyscript/test_components.py | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/test_pyscript/pyscript_components/root_error.py diff --git a/src/reactpy/executors/pyscript/layout_handler.py b/src/reactpy/executors/pyscript/layout_handler.py index 1c2e6367a..1a49c75dc 100644 --- a/src/reactpy/executors/pyscript/layout_handler.py +++ b/src/reactpy/executors/pyscript/layout_handler.py @@ -28,6 +28,8 @@ def update_model(update, root_model): if update["path"]: set_pointer(root_model, update["path"], update["model"]) else: + # clear old keys first, dict.update() alone would keep stale "children" around when the new model doesn't have any + root_model.clear() root_model.update(update["model"]) def render_html(self, layout, model): diff --git a/tests/test_pyscript/pyscript_components/root_error.py b/tests/test_pyscript/pyscript_components/root_error.py new file mode 100644 index 000000000..52c990b67 --- /dev/null +++ b/tests/test_pyscript/pyscript_components/root_error.py @@ -0,0 +1,20 @@ +from reactpy import component, hooks, html + + +@component +def root(): + count, set_count = hooks.use_state(0) + + def increment(event): + set_count(count + 1) + + # crash on purpose after a few clicks, same as the real bug report + if count == 3: + raise ValueError("This error should hide the root component") + + return html.div( + html.button( + {"onClick": increment, "id": "incr", "data-count": count}, "Increment" + ), + html.p(f"PyScript Count: {count}"), + ) diff --git a/tests/test_pyscript/test_components.py b/tests/test_pyscript/test_components.py index dac7de113..80bdd6c8d 100644 --- a/tests/test_pyscript/test_components.py +++ b/tests/test_pyscript/test_components.py @@ -68,6 +68,33 @@ def CustomRootName(): await display.page.wait_for_selector("#incr[data-count='3']") +async def test_root_component_error_hides_component(display: DisplayFixture): + """A crash in the root render should hide it, not leave stale content stuck + on the page.""" + + @reactpy.component + def Counter(): + return pyscript_component( + Path(__file__).parent / "pyscript_components" / "root_error.py", + initial=html.div({"id": "loading"}, "Loading..."), + ) + + await display.show(Counter) + + await display.page.wait_for_selector("#loading") + await display.page.wait_for_selector("#incr") + + await display.page.click("#incr") + await display.page.wait_for_selector("#incr[data-count='1']") + + await display.page.click("#incr") + await display.page.wait_for_selector("#incr[data-count='2']") + + # this click flips count to 3 -> root component raises -> button should vanish + await display.page.click("#incr") + await display.page.wait_for_selector("#incr", state="detached") + + def test_bad_file_path(): with pytest.raises(ValueError): pyscript_component(initial=html.div({"id": "loading"}, "Loading...")).render()