Forráskód Böngészése

Fix router state after client side navigation (#4383)

* Fix router state after client side navigation

* add client side navigation to the fullstack routing spec

* fix playwright test
Evan Almloff 1 napja
szülő
commit
b252528722

+ 3 - 5
packages/fullstack-hooks/src/history.rs

@@ -2,10 +2,8 @@
 
 use std::cell::OnceCell;
 
-use dioxus_core::{
-    prelude::{generation, queue_effect},
-    schedule_update,
-};
+use dioxus_core::{prelude::queue_effect, schedule_update};
+use dioxus_fullstack_protocol::is_hydrating;
 use dioxus_history::History;
 
 // If we are currently in a scope and this is the first run then queue a rerender
@@ -14,7 +12,7 @@ fn match_hydration<O>(
     during_hydration: impl FnOnce() -> O,
     after_hydration: impl FnOnce() -> O,
 ) -> O {
-    if generation() == 0 {
+    if is_hydrating() {
         let update = schedule_update();
         queue_effect(move || update());
         during_hydration()

+ 13 - 0
packages/fullstack-protocol/src/lib.rs

@@ -144,6 +144,19 @@ impl<T> SerializeContextEntry<T> {
     }
 }
 
+/// Check if the client is currently rendering a component for hydration. Always returns true on the server.
+pub fn is_hydrating() -> bool {
+    #[cfg(feature = "web")]
+    {
+        // On the client, we can check if the context is set
+        CONTEXT.with(|context| context.borrow().is_some())
+    }
+    #[cfg(not(feature = "web"))]
+    {
+        true
+    }
+}
+
 /// Get or insert the current serialize context. On the client, the hydration context this returns
 /// will always return `TakeDataError::DataNotAvailable` if hydration of the current chunk is finished.
 pub fn serialize_context() -> HydrationContext {

+ 30 - 0
packages/playwright-tests/fullstack-routing.spec.js

@@ -51,3 +51,33 @@ test("unknown route", async ({ request }) => {
 
   expect(response.status()).toBe(404);
 });
+
+// Clicking the link on the home page should navigate to the blog route
+test("click blog link", async ({ page }) => {
+  await page.goto("http://localhost:8888");
+
+  // Click the link to the blog route
+  await page.locator('a').click();
+
+  // Wait for navigation to complete
+  await page.waitForURL("http://localhost:8888/blog/1/");
+
+  // Check that the blog page is displayed
+  const text = await page.textContent("body");
+  expect(text).toContain("id: 1");
+});
+
+// Clicking the link on the blog page should navigate back to the home route
+test("click home link from blog", async ({ page }) => {
+  await page.goto("http://localhost:8888/blog/1");
+
+  // Click the link to the home route
+  await page.locator('a').click();
+
+  // Wait for navigation to complete
+  await page.waitForURL("http://localhost:8888");
+
+  // Check that the home page is displayed
+  const text = await page.textContent("body");
+  expect(text).toContain("Home");
+});

+ 7 - 0
packages/playwright-tests/fullstack-routing/src/main.rs

@@ -36,6 +36,9 @@ enum Route {
 
 #[component]
 fn Blog(id: i32) -> Element {
+    let route: Route = use_route();
+    assert_eq!(route, Route::Blog { id });
+
     rsx! {
         Link { to: Route::Home {}, "Go home" }
         "id: {id}"
@@ -51,8 +54,12 @@ fn ThrowsError() -> Element {
 
 #[component]
 fn Home() -> Element {
+    let route: Route = use_route();
+    assert_eq!(route, Route::Home);
+
     rsx! {
         "Home"
+        Link { to: Route::Blog { id: 1 }, "Go to blog 1" }
     }
 }