Skip to content

Commit bee18a5

Browse files
committed
server-side link component
1 parent b8efe54 commit bee18a5

File tree

6 files changed

+36
-53
lines changed

6 files changed

+36
-53
lines changed

src/js/package-lock.json

-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/js/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,15 @@
1818
"test": "echo \"Error: no test specified\" && exit 1"
1919
},
2020
"devDependencies": {
21-
"prettier": "^2.2.1",
2221
"eslint": "^8.38.0",
2322
"eslint-plugin-react": "^7.32.2",
23+
"prettier": "^2.2.1",
2424
"rollup": "^2.35.1",
2525
"rollup-plugin-commonjs": "^10.1.0",
2626
"rollup-plugin-node-resolve": "^5.2.0",
2727
"rollup-plugin-replace": "^2.2.0"
2828
},
2929
"dependencies": {
30-
"htm": "^3.0.4",
3130
"react": "^17.0.1",
3231
"react-dom": "^17.0.1"
3332
}

src/js/src/index.js

+19-35
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,28 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
3-
import htm from "htm";
4-
5-
const html = htm.bind(React.createElement);
63

74
export function bind(node) {
8-
return {
9-
create: (type, props, children) =>
10-
React.createElement(type, props, ...children),
11-
render: (element) => {
12-
ReactDOM.render(element, node);
13-
},
14-
unmount: () => ReactDOM.unmountComponentAtNode(node),
15-
};
16-
}
17-
18-
export function History({ onChange }) {
19-
// capture changes to the browser's history
20-
React.useEffect(() => {
21-
const listener = () => {
22-
onChange({
23-
pathname: window.location.pathname,
24-
search: window.location.search,
25-
});
5+
return {
6+
create: (type, props, children) =>
7+
React.createElement(type, props, ...children),
8+
render: (element) => {
9+
ReactDOM.render(element, node);
10+
},
11+
unmount: () => ReactDOM.unmountComponentAtNode(node),
2612
};
27-
window.addEventListener("popstate", listener);
28-
return () => window.removeEventListener("popstate", listener);
29-
});
30-
return null;
3113
}
3214

33-
export function Link({ to, onClick, children, ...props }) {
34-
const handleClick = (event) => {
35-
event.preventDefault();
36-
window.history.pushState({}, to, new URL(to, window.location));
37-
onClick({
38-
pathname: window.location.pathname,
39-
search: window.location.search,
15+
export function History({ onChange }) {
16+
// capture changes to the browser's history
17+
React.useEffect(() => {
18+
const listener = () => {
19+
onChange({
20+
pathname: window.location.pathname,
21+
search: window.location.search,
22+
});
23+
};
24+
window.addEventListener("popstate", listener);
25+
return () => window.removeEventListener("popstate", listener);
4026
});
41-
};
42-
43-
return html`<a href=${to} onClick=${handleClick} ...${props}>${children}</a>`;
27+
return null;
4428
}

src/reactpy_router/core.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pathlib import Path
88
from typing import Any, Callable, Iterator, Literal, Sequence, TypeVar
99
from urllib.parse import parse_qs
10+
from uuid import uuid4
1011

1112
from reactpy import (
1213
component,
@@ -68,7 +69,7 @@ def router_component(
6869
_route_state_context(element, value=_RouteState(set_location, params)),
6970
value=Connection(old_conn.scope, location, old_conn.carrier),
7071
),
71-
_history({"on_change": lambda event: set_location(Location(**event))}),
72+
History({"on_change": lambda event: set_location(Location(**event))}),
7273
key=location.pathname + select,
7374
)
7475

@@ -79,12 +80,14 @@ def router_component(
7980
def link(*children: VdomChild, to: str, **attributes: Any) -> VdomDict:
8081
"""A component that renders a link to the given path"""
8182
set_location = _use_route_state().set_location
83+
uuid = uuid4().hex
8284
attrs = {
8385
**attributes,
8486
"to": to,
8587
"onClick": lambda event: set_location(Location(**event)),
88+
"id": uuid,
8689
}
87-
return _link(attrs, *children)
90+
return html._(html.a(attrs, *children), html.script(link_js_content.replace("UUID", uuid)))
8891

8992

9093
def use_params() -> dict[str, Any]:
@@ -150,10 +153,11 @@ def _match_route(
150153
return matches or None
151154

152155

153-
_link, _history = export(
156+
History = export(
154157
module_from_file("reactpy-router", file=Path(__file__).parent / "bundle.js"),
155-
("Link", "History"),
158+
("History"),
156159
)
160+
link_js_content = (Path(__file__).parent / "js" / "Link.js").read_text(encoding="utf-8")
157161

158162

159163
@dataclass

src/reactpy_router/js/Link.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
document.getElementById("UUID").addEventListener("click", (event) => {
2+
event.preventDefault();
3+
window.history.pushState({}, to, new URL(to, window.location));
4+
onClick({
5+
pathname: window.location.pathname,
6+
search: window.location.search,
7+
});
8+
});

src/reactpy_router/routers.py

-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@
1111
browser_router = create_router(Resolver)
1212
"""This is the recommended router for all ReactPy Router web projects.
1313
It uses the DOM History API to update the URL and manage the history stack."""
14-
# TODO: Check if this is true. If not, make it true.

0 commit comments

Comments
 (0)