diff --git a/CHANGELOG.md b/CHANGELOG.md index 26a01ca..f9cc516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ Using the following categories, list your changes in this order: ## [Unreleased] -- Nothing (yet)! +- Fix double rendering issue on initial page load ## [1.0.2] - 2024-10-24 diff --git a/src/reactpy_router/routers.py b/src/reactpy_router/routers.py index abdbc5e..d8e75f2 100644 --- a/src/reactpy_router/routers.py +++ b/src/reactpy_router/routers.py @@ -74,13 +74,19 @@ def router( match = use_memo(lambda: _match_route(resolvers, location, select="first")) if match: - route_elements = [ - _route_state_context( - element, - value=RouteState(set_location, params), - ) - for element, params in match - ] + if first_load: + # We need skip rendering the application on 'first_load' to avoid + # rendering it twice. The second render occurs following + # the impending on_history_change event + route_elements = [] + else: + route_elements = [ + _route_state_context( + element, + value=RouteState(set_location, params), + ) + for element, params in match + ] def on_history_change(event: dict[str, Any]) -> None: """Callback function used within the JavaScript `History` component.""" diff --git a/tests/test_rendering.py b/tests/test_rendering.py new file mode 100644 index 0000000..6a78ecc --- /dev/null +++ b/tests/test_rendering.py @@ -0,0 +1,60 @@ +import pytest +from reactpy import component, html +from reactpy.testing import DisplayFixture + +from reactpy_router import browser_router, route + +from .utils import page_load_complete + + +@pytest.mark.anyio +async def test_router_simple(display: DisplayFixture): + """Confirm the number of rendering operations when new pages are first loaded""" + root_render_count = 0 + home_page_render_count = 0 + not_found_render_count = 0 + + @component + def root(): + nonlocal root_render_count + root_render_count += 1 + + @component + def home_page(): + nonlocal home_page_render_count + home_page_render_count += 1 + return html.h1("Home Page 🏠") + + @component + def not_found(): + nonlocal not_found_render_count + not_found_render_count += 1 + return html.h1("Missing Link 🔗‍💥") + + return browser_router( + route("/", home_page()), + route("{404:any}", not_found()), + ) + + await display.show(root) + await page_load_complete(display.page) + + assert root_render_count == 1 + assert home_page_render_count == 1 + assert not_found_render_count == 0 + + await display.goto("/xxx") + await page_load_complete(display.page) + + assert root_render_count == 2 + assert home_page_render_count == 1 + assert not_found_render_count == 1 + + await display.goto("/yyy") + await page_load_complete(display.page) + + assert root_render_count == 3 + assert home_page_render_count == 1 + assert not_found_render_count == 2 + + assert True diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..2b7bc2e --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,7 @@ +from playwright.async_api._generated import Page + + +async def page_load_complete(page: Page) -> None: + """Only return when network is idle and DOM has loaded""" + await page.wait_for_load_state("networkidle") + await page.wait_for_load_state("domcontentloaded")