Skip to content

Commit 9c02410

Browse files
committed
fix url router
1 parent 52bbbce commit 9c02410

File tree

5 files changed

+69
-12
lines changed

5 files changed

+69
-12
lines changed

src/js/src/client.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
import {
2-
ReactPyClient,
2+
BaseReactPyClient,
33
createReconnectingWebSocket,
44
type GenericReactPyClientProps,
5+
type ReactPyClientInterface,
6+
type ReactPyModule,
7+
type ReactPyUrls,
58
} from "@reactpy/client";
69

7-
export class ReactPyDjangoClient extends ReactPyClient {
10+
export class ReactPyDjangoClient
11+
extends BaseReactPyClient
12+
implements ReactPyClientInterface
13+
{
14+
urls: ReactPyUrls;
15+
socket: { current?: WebSocket };
16+
mountElement: HTMLElement;
817
prerenderElement: HTMLElement | null = null;
918
offlineElement: HTMLElement | null = null;
19+
private readonly messageQueue: any[] = [];
1020

1121
constructor(props: GenericReactPyClientProps) {
12-
super(props);
22+
super();
23+
24+
this.urls = props.urls;
25+
this.mountElement = props.mountElement;
1326
this.prerenderElement = document.getElementById(
1427
props.mountElement.id + "-prerender",
1528
);
1629
this.offlineElement = document.getElementById(
1730
props.mountElement.id + "-offline",
1831
);
32+
1933
this.socket = createReconnectingWebSocket({
2034
url: this.urls.componentUrl,
2135
readyPromise: this.ready,
@@ -42,4 +56,19 @@ export class ReactPyDjangoClient extends ReactPyClient {
4256
},
4357
});
4458
}
59+
60+
sendMessage(message: any): void {
61+
if (
62+
this.socket.current &&
63+
this.socket.current.readyState === WebSocket.OPEN
64+
) {
65+
this.socket.current.send(JSON.stringify(message));
66+
} else {
67+
this.messageQueue.push(message);
68+
}
69+
}
70+
71+
loadModule(moduleName: string): Promise<ReactPyModule> {
72+
return import(`${this.urls.jsModulesPath}${moduleName}`);
73+
}
4574
}

src/js/src/mount.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ export function mountComponent(
2020
const componentUrl = new URL(`${wsOrigin}/${urlPrefix}/${componentPath}`);
2121

2222
// Embed the initial HTTP path into the WebSocket URL
23-
componentUrl.searchParams.append("http_path", window.location.pathname);
23+
componentUrl.searchParams.append("path", window.location.pathname);
2424
if (window.location.search) {
25-
componentUrl.searchParams.append(
26-
"http_query_string",
27-
window.location.search,
28-
);
25+
componentUrl.searchParams.append("qs", window.location.search);
2926
}
3027

3128
// HTTP route for JavaScript modules

src/reactpy_django/websocket/consumer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ async def run_dispatcher(self):
156156
has_args = scope["url_route"]["kwargs"].get("has_args")
157157
scope["reactpy"] = {"id": str(uuid)}
158158
query_string = parse_qs(scope["query_string"].decode(), strict_parsing=True)
159-
http_path = query_string.get("http_path", [""])[0]
160-
http_query_string = query_string.get("http_query_string", [""])[0]
159+
http_path = query_string.get("path", [""])[0]
160+
http_query_string = query_string.get("qs", [""])[0]
161161
self.recv_queue = asyncio.Queue()
162162
connection = Connection( # For `use_connection`
163163
scope=scope,

tests/test_app/router/components.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
from reactpy import component, html, use_location
2-
from reactpy_router import route, use_params, use_search_params
1+
from uuid import uuid4
2+
3+
from reactpy import component, html, use_location, use_state
4+
from reactpy_router import link, route, use_params, use_search_params
35
from reactpy_router.types import Route
46

57
from reactpy_django.router import django_router
@@ -27,6 +29,21 @@ def show_route(path: str, *children: Route) -> Route:
2729
return route(path, display_params(path), *children)
2830

2931

32+
@component
33+
def next_page():
34+
url_params = use_params()
35+
state, set_state = use_state(uuid4)
36+
page = url_params.get("page", 0)
37+
next_page = page + 1
38+
return html.fragment(
39+
display_params("/router/next/<int:page>/"),
40+
html.div({"id": "router-uuid", "data-uuid": state.hex}, f"UUID: {state.hex}"),
41+
html.button(
42+
link({"to": f"/router/next/{next_page}/"}, "Next Page"),
43+
),
44+
)
45+
46+
3047
@component
3148
def main():
3249
return django_router(
@@ -39,4 +56,5 @@ def main():
3956
show_route("/router/uuid/<uuid:value>/"),
4057
show_route("/router/any/<any:name>"),
4158
show_route("/router/two/<int:value>/<str:value2>/"),
59+
route("/router/next/<int:page>/", next_page()),
4260
)

tests/test_app/tests/test_components.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,19 @@ def test_url_router_int_and_string(self):
535535
string = self.page.query_selector("#router-string")
536536
assert string.text_content() == "/router/two/<int:value>/<str:value2>/"
537537

538+
def test_url_router_navigation_state(self):
539+
self.page.goto(f"{self.live_server_url}/router/next/1/")
540+
uuid1 = self.page.wait_for_selector("#router-uuid").get_attribute("data-uuid")
541+
self.page.locator("button").click()
542+
self.page.wait_for_selector(f"#router-path[data-path='/router/next/2/']")
543+
uuid2 = self.page.wait_for_selector("#router-uuid").get_attribute("data-uuid")
544+
assert uuid1 == uuid2
545+
self.page.go_back()
546+
self.page.wait_for_selector(f"#router-path[data-path='/router/next/1/']")
547+
uuid3 = self.page.wait_for_selector("#router-uuid").get_attribute("data-uuid")
548+
# When going back, it should also be a new mount if navigation always remounts
549+
assert uuid1 == uuid3
550+
538551
#######################
539552
# Channel Layer Tests #
540553
#######################

0 commit comments

Comments
 (0)