浏览代码

Support replacing route on redirect (#3072)

* Support replacing route on redirect

Support next/router `.replace` interface to change page without creating a
history entry.

* test_event: include test cases for new "replace" kwarg
Masen Furer 1 年之前
父节点
当前提交
bc6f0f70cb
共有 3 个文件被更改,包括 47 次插入14 次删除
  1. 7 2
      reflex/.templates/web/utils/state.js
  2. 11 2
      reflex/event.py
  3. 29 10
      tests/test_event.py

+ 7 - 2
reflex/.templates/web/utils/state.js

@@ -126,8 +126,13 @@ export const applyDelta = (state, delta) => {
 export const applyEvent = async (event, socket) => {
   // Handle special events
   if (event.name == "_redirect") {
-    if (event.payload.external) window.open(event.payload.path, "_blank");
-    else Router.push(event.payload.path);
+    if (event.payload.external) {
+      window.open(event.payload.path, "_blank");
+    } else if (event.payload.replace) {
+      Router.replace(event.payload.path);
+    } else {
+      Router.push(event.payload.path);
+    }
     return false;
   }
 

+ 11 - 2
reflex/event.py

@@ -467,18 +467,27 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
     )
 
 
-def redirect(path: str | Var[str], external: Optional[bool] = False) -> EventSpec:
+def redirect(
+    path: str | Var[str],
+    external: Optional[bool] = False,
+    replace: Optional[bool] = False,
+) -> EventSpec:
     """Redirect to a new path.
 
     Args:
         path: The path to redirect to.
         external: Whether to open in new tab or not.
+        replace: If True, the current page will not create a new history entry.
 
     Returns:
         An event to redirect to the path.
     """
     return server_side(
-        "_redirect", get_fn_signature(redirect), path=path, external=external
+        "_redirect",
+        get_fn_signature(redirect),
+        path=path,
+        external=external,
+        replace=replace,
     )
 
 

+ 29 - 10
tests/test_event.py

@@ -158,12 +158,29 @@ def test_fix_events(arg1, arg2):
 @pytest.mark.parametrize(
     "input,output",
     [
-        (("/path", None), 'Event("_redirect", {path:`/path`,external:false})'),
-        (("/path", True), 'Event("_redirect", {path:`/path`,external:true})'),
-        (("/path", False), 'Event("_redirect", {path:`/path`,external:false})'),
         (
-            (Var.create_safe("path"), None),
-            'Event("_redirect", {path:path,external:false})',
+            ("/path", None, None),
+            'Event("_redirect", {path:`/path`,external:false,replace:false})',
+        ),
+        (
+            ("/path", True, None),
+            'Event("_redirect", {path:`/path`,external:true,replace:false})',
+        ),
+        (
+            ("/path", False, None),
+            'Event("_redirect", {path:`/path`,external:false,replace:false})',
+        ),
+        (
+            (Var.create_safe("path"), None, None),
+            'Event("_redirect", {path:path,external:false,replace:false})',
+        ),
+        (
+            ("/path", None, True),
+            'Event("_redirect", {path:`/path`,external:false,replace:true})',
+        ),
+        (
+            ("/path", True, True),
+            'Event("_redirect", {path:`/path`,external:true,replace:true})',
         ),
     ],
 )
@@ -174,11 +191,13 @@ def test_event_redirect(input, output):
         input: The input for running the test.
         output: The expected output to validate the test.
     """
-    path, external = input
-    if external is None:
-        spec = event.redirect(path)
-    else:
-        spec = event.redirect(path, external=external)
+    path, external, replace = input
+    kwargs = {}
+    if external is not None:
+        kwargs["external"] = external
+    if replace is not None:
+        kwargs["replace"] = replace
+    spec = event.redirect(path, **kwargs)
     assert isinstance(spec, EventSpec)
     assert spec.handler.fn.__qualname__ == "_redirect"