4
4
5
5
from dataclasses import replace
6
6
from logging import getLogger
7
- from typing import TYPE_CHECKING , Any , Literal , cast
7
+ from typing import TYPE_CHECKING , Any , cast
8
8
9
9
from reactpy import component , use_memo , use_state
10
10
from reactpy .backend .types import Connection , Location
14
14
from reactpy_router .components import History
15
15
from reactpy_router .hooks import RouteState , _route_state_context
16
16
from reactpy_router .resolvers import StarletteResolver
17
+ from reactpy_router .types import MatchedRoute
17
18
18
19
if TYPE_CHECKING :
19
20
from collections .abc import Iterator , Sequence
@@ -57,36 +58,27 @@ def router(
57
58
* routes : Route ,
58
59
resolver : Resolver [Route ],
59
60
) -> VdomDict | None :
60
- """A component that renders matching route(s) using the given resolver.
61
+ """A component that renders matching route using the given resolver.
61
62
62
- This typically should never be used by a user . Instead, use `create_router` if creating
63
+ User notice: This component typically should never be used. Instead, use `create_router` if creating
63
64
a custom routing engine."""
64
65
65
- old_conn = use_connection ()
66
- location , set_location = use_state (old_conn .location )
67
- first_load , set_first_load = use_state (True )
68
-
66
+ old_connection = use_connection ()
67
+ location , set_location = use_state (cast (Location | None , None ))
69
68
resolvers = use_memo (
70
69
lambda : tuple (map (resolver , _iter_routes (routes ))),
71
70
dependencies = (resolver , hash (routes )),
72
71
)
73
-
74
- match = use_memo (lambda : _match_route (resolvers , location , select = "first" ))
72
+ route_element = None
73
+ match = use_memo (lambda : _match_route (resolvers , location or old_connection . location ))
75
74
76
75
if match :
77
- if first_load :
78
- # We need skip rendering the application on 'first_load' to avoid
79
- # rendering it twice. The second render follows the on_history_change event
80
- route_elements = []
81
- set_first_load (False )
82
- else :
83
- route_elements = [
84
- _route_state_context (
85
- element ,
86
- value = RouteState (set_location , params ),
87
- )
88
- for element , params in match
89
- ]
76
+ # Skip rendering until ReactPy-Router knows what URL the page is on.
77
+ if location :
78
+ route_element = _route_state_context (
79
+ match .element ,
80
+ value = RouteState (set_location , match .params ),
81
+ )
90
82
91
83
def on_history_change (event : dict [str , Any ]) -> None :
92
84
"""Callback function used within the JavaScript `History` component."""
@@ -96,8 +88,8 @@ def on_history_change(event: dict[str, Any]) -> None:
96
88
97
89
return ConnectionContext (
98
90
History ({"onHistoryChangeCallback" : on_history_change }), # type: ignore[return-value]
99
- * route_elements ,
100
- value = Connection (old_conn .scope , location , old_conn .carrier ),
91
+ route_element ,
92
+ value = Connection (old_connection .scope , location or old_connection . location , old_connection .carrier ),
101
93
)
102
94
103
95
return None
@@ -110,9 +102,9 @@ def _iter_routes(routes: Sequence[Route]) -> Iterator[Route]:
110
102
yield parent
111
103
112
104
113
- def _add_route_key (match : tuple [ Any , dict [ str , Any ]] , key : str | int ) -> Any :
105
+ def _add_route_key (match : MatchedRoute , key : str | int ) -> Any :
114
106
"""Add a key to the VDOM or component on the current route, if it doesn't already have one."""
115
- element , _params = match
107
+ element = match . element
116
108
if hasattr (element , "render" ) and not element .key :
117
109
element = cast (ComponentType , element )
118
110
element .key = key
@@ -125,24 +117,12 @@ def _add_route_key(match: tuple[Any, dict[str, Any]], key: str | int) -> Any:
125
117
def _match_route (
126
118
compiled_routes : Sequence [CompiledRoute ],
127
119
location : Location ,
128
- select : Literal ["first" , "all" ],
129
- ) -> list [tuple [Any , dict [str , Any ]]]:
130
- matches = []
131
-
120
+ ) -> MatchedRoute | None :
132
121
for resolver in compiled_routes :
133
122
match = resolver .resolve (location .pathname )
134
123
if match is not None :
135
- if select == "first" :
136
- return [_add_route_key (match , resolver .key )]
124
+ return _add_route_key (match , resolver .key )
137
125
138
- # Matching multiple routes is disabled since `react-router` no longer supports multiple
139
- # matches via the `Route` component. However, it's kept here to support future changes
140
- # or third-party routers.
141
- # TODO: The `resolver.key` value has edge cases where it is not unique enough to use as
142
- # a key here. We can potentially fix this by throwing errors for duplicate identical routes.
143
- matches .append (_add_route_key (match , resolver .key )) # pragma: no cover
126
+ _logger .debug ("No matching route found for %s" , location .pathname )
144
127
145
- if not matches :
146
- _logger .debug ("No matching route found for %s" , location .pathname )
147
-
148
- return matches
128
+ return None
0 commit comments