Skip to content

Commit 281cf25

Browse files
committed
fix client storage
1 parent acbe19b commit 281cf25

6 files changed

Lines changed: 77 additions & 36 deletions

File tree

reflex/.templates/web/app/routes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { route } from "@react-router/dev/routes";
22
import { flatRoutes } from "@react-router/fs-routes";
33

44
export default [
5-
route("*", "routes/404.js"),
5+
route("*", "routes/[404]_._index.js"),
66
...(await flatRoutes({
7-
ignoredRouteFiles: ["routes/404.js"],
7+
ignoredRouteFiles: ["routes/\\[404\\]_._index.js"],
88
})),
99
];

reflex/.templates/web/utils/state.js

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import env from "$/env.json";
66
import reflexEnvironment from "$/reflex.json";
77
import Cookies from "universal-cookie";
88
import { useEffect, useRef, useState } from "react";
9-
import { useLocation, useNavigate, useSearchParams } from "react-router";
9+
import {
10+
useLocation,
11+
useNavigate,
12+
useSearchParams,
13+
useParams,
14+
} from "react-router";
1015
import {
1116
initialEvents,
1217
initialState,
@@ -159,14 +164,20 @@ export const evalReactComponent = async (component) => {
159164
* @param event The event to queue.
160165
* @param socket The socket object to send the event on.
161166
* @param navigate The navigate function from React Router
167+
* @param params The params object from React Router
162168
*
163169
* @returns Adds event to queue and processes it if websocket exits, does nothing otherwise.
164170
*/
165-
export const queueEventIfSocketExists = async (events, socket, navigate) => {
171+
export const queueEventIfSocketExists = async (
172+
events,
173+
socket,
174+
navigate,
175+
params,
176+
) => {
166177
if (!socket) {
167178
return;
168179
}
169-
await queueEvents(events, socket, navigate);
180+
await queueEvents(events, socket, navigate, params);
170181
};
171182

172183
/**
@@ -189,10 +200,11 @@ function urlFrom(string) {
189200
* @param event The event to send.
190201
* @param socket The socket object to send the event on.
191202
* @param navigate The navigate function from useNavigate
203+
* @param params The params object from useParams
192204
*
193205
* @returns True if the event was sent, false if it was handled locally.
194206
*/
195-
export const applyEvent = async (event, socket, navigate) => {
207+
export const applyEvent = async (event, socket, navigate, params) => {
196208
// Handle special events
197209
if (event.name == "_redirect") {
198210
if ((event.payload.path ?? undefined) === undefined) {
@@ -223,31 +235,31 @@ export const applyEvent = async (event, socket, navigate) => {
223235

224236
if (event.name == "_remove_cookie") {
225237
cookies.remove(event.payload.key, { ...event.payload.options });
226-
queueEventIfSocketExists(initialEvents(), socket, navigate);
238+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
227239
return false;
228240
}
229241

230242
if (event.name == "_clear_local_storage") {
231243
localStorage.clear();
232-
queueEventIfSocketExists(initialEvents(), socket, navigate);
244+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
233245
return false;
234246
}
235247

236248
if (event.name == "_remove_local_storage") {
237249
localStorage.removeItem(event.payload.key);
238-
queueEventIfSocketExists(initialEvents(), socket, navigate);
250+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
239251
return false;
240252
}
241253

242254
if (event.name == "_clear_session_storage") {
243255
sessionStorage.clear();
244-
queueEvents(initialEvents(), socket, navigate);
256+
queueEvents(initialEvents(), socket, navigate, params);
245257
return false;
246258
}
247259

248260
if (event.name == "_remove_session_storage") {
249261
sessionStorage.removeItem(event.payload.key);
250-
queueEvents(initialEvents(), socket, navigate);
262+
queueEvents(initialEvents(), socket, navigate, params);
251263
return false;
252264
}
253265

@@ -355,7 +367,10 @@ export const applyEvent = async (event, socket, navigate) => {
355367
// Since we don't have router directly, we need to get info from our hooks
356368
event.router_data = {
357369
pathname: window.location.pathname,
358-
query: Object.fromEntries(new URLSearchParams(window.location.search)),
370+
query: {
371+
...Object.fromEntries(new URLSearchParams(window.location.search)),
372+
...params,
373+
},
359374
asPath: window.location.pathname + window.location.search,
360375
};
361376
}
@@ -374,15 +389,16 @@ export const applyEvent = async (event, socket, navigate) => {
374389
* @param event The current event.
375390
* @param socket The socket object to send the response event(s) on.
376391
* @param navigate The navigate function from React Router
392+
* @param params The params object from React Router
377393
*
378394
* @returns Whether the event was sent.
379395
*/
380-
export const applyRestEvent = async (event, socket, navigate) => {
396+
export const applyRestEvent = async (event, socket, navigate, params) => {
381397
let eventSent = false;
382398
if (event.handler === "uploadFiles") {
383399
if (event.payload.files === undefined || event.payload.files.length === 0) {
384400
// Submit the event over the websocket to trigger the event handler.
385-
return await applyEvent(Event(event.name), socket, navigate);
401+
return await applyEvent(Event(event.name), socket, navigate, params);
386402
}
387403

388404
// Start upload, but do not wait for it, which would block other events.
@@ -404,8 +420,15 @@ export const applyRestEvent = async (event, socket, navigate) => {
404420
* @param socket The socket object to send the event on.
405421
* @param prepend Whether to place the events at the beginning of the queue.
406422
* @param navigate The navigate function from React Router
423+
* @param params The params object from React Router
407424
*/
408-
export const queueEvents = async (events, socket, prepend, navigate) => {
425+
export const queueEvents = async (
426+
events,
427+
socket,
428+
prepend,
429+
navigate,
430+
params,
431+
) => {
409432
if (prepend) {
410433
// Drain the existing queue and place it after the given events.
411434
events = [
@@ -416,15 +439,16 @@ export const queueEvents = async (events, socket, prepend, navigate) => {
416439
];
417440
}
418441
event_queue.push(...events.filter((e) => e !== undefined && e !== null));
419-
await processEvent(socket.current, navigate);
442+
await processEvent(socket.current, navigate, params);
420443
};
421444

422445
/**
423446
* Process an event off the event queue.
424447
* @param socket The socket object to send the event on.
425448
* @param navigate The navigate function from React Router
449+
* @param params The params object from React Router
426450
*/
427-
export const processEvent = async (socket, navigate) => {
451+
export const processEvent = async (socket, navigate, params) => {
428452
// Only proceed if the socket is up and no event in the queue uses state, otherwise we throw the event into the void
429453
if (!socket && isStateful()) {
430454
return;
@@ -444,16 +468,16 @@ export const processEvent = async (socket, navigate) => {
444468
let eventSent = false;
445469
// Process events with handlers via REST and all others via websockets.
446470
if (event.handler) {
447-
eventSent = await applyRestEvent(event, socket, navigate);
471+
eventSent = await applyRestEvent(event, socket, navigate, params);
448472
} else {
449-
eventSent = await applyEvent(event, socket, navigate);
473+
eventSent = await applyEvent(event, socket, navigate, params);
450474
}
451475
// If no event was sent, set processing to false.
452476
if (!eventSent) {
453477
event_processing = false;
454478
// recursively call processEvent to drain the queue, since there is
455479
// no state update to trigger the useEffect event loop.
456-
await processEvent(socket, navigate);
480+
await processEvent(socket, navigate, params);
457481
}
458482
};
459483

@@ -465,6 +489,7 @@ export const processEvent = async (socket, navigate) => {
465489
* @param setConnectErrors The function to update connection error value.
466490
* @param client_storage The client storage object from context.js
467491
* @param navigate The navigate function from React Router
492+
* @param params The params object from React Router
468493
*/
469494
export const connect = async (
470495
socket,
@@ -473,6 +498,7 @@ export const connect = async (
473498
setConnectErrors,
474499
client_storage = {},
475500
navigate,
501+
params = {},
476502
) => {
477503
// Get backend URL object from the endpoint.
478504
const endpoint = getBackendURL(EVENTURL);
@@ -547,12 +573,12 @@ export const connect = async (
547573
applyClientStorageDelta(client_storage, update.delta);
548574
event_processing = !update.final;
549575
if (update.events) {
550-
queueEvents(update.events, socket, false, navigate);
576+
queueEvents(update.events, socket, false, navigate, params);
551577
}
552578
});
553579
socket.current.on("reload", async (event) => {
554580
event_processing = false;
555-
queueEvents([...initialEvents(), event], socket, true, navigate);
581+
queueEvents([...initialEvents(), event], socket, true, navigate, params);
556582
});
557583

558584
document.addEventListener("visibilitychange", checkVisibility);
@@ -801,6 +827,7 @@ export const useEventLoop = (
801827
const socket = useRef(null);
802828
const location = useLocation();
803829
const navigate = useNavigate();
830+
const params = useParams();
804831
const prevLocationRef = useRef(location);
805832
const [searchParams] = useSearchParams();
806833
const [connectErrors, setConnectErrors] = useState([]);
@@ -842,11 +869,11 @@ export const useEventLoop = (
842869
// If debounce is used, queue the events after some delay
843870
debounce(
844871
combined_name,
845-
() => queueEvents(_events, socket, false, navigate),
872+
() => queueEvents(_events, socket, false, navigate, params),
846873
event_actions.debounce,
847874
);
848875
} else {
849-
queueEvents(_events, socket, false, navigate);
876+
queueEvents(_events, socket, false, navigate, params);
850877
}
851878
};
852879

@@ -858,13 +885,14 @@ export const useEventLoop = (
858885
...e,
859886
router_data: {
860887
pathname: location.pathname,
861-
query: Object.fromEntries(searchParams.entries()),
888+
query: { ...Object.fromEntries(searchParams.entries()), ...params },
862889
asPath: location.pathname + location.search,
863890
},
864891
})),
865892
socket,
866893
true,
867894
navigate,
895+
params,
868896
);
869897
sentHydrate.current = true;
870898
}
@@ -912,6 +940,7 @@ export const useEventLoop = (
912940
setConnectErrors,
913941
client_storage,
914942
navigate,
943+
params,
915944
);
916945
}
917946
}
@@ -933,7 +962,7 @@ export const useEventLoop = (
933962
(async () => {
934963
// Process all outstanding events.
935964
while (event_queue.length > 0 && !event_processing) {
936-
await processEvent(socket.current, navigate);
965+
await processEvent(socket.current, navigate, params);
937966
}
938967
})();
939968
});

reflex/compiler/utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,6 @@ def _format_route_part(part: str) -> str:
401401
def _path_to_file_stem(path: str) -> str:
402402
if path == "index":
403403
return "_index"
404-
if path.removeprefix("/") == "404":
405-
return "404"
406404
path = path if path != "index" else "/"
407405
return (
408406
".".join([_format_route_part(part) for part in path.split("/")]) + "._index"

reflex/constants/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class ReactRouter(Javascript):
225225
"""Constants related to React Router."""
226226

227227
# The react router config file
228-
CONFIG_FILE = "react-router-config.js"
228+
CONFIG_FILE = "react-router.config.js"
229229

230230
# Regex to check for message displayed when frontend comes up
231231
FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"

reflex/constants/installer.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ def _determine_nextjs_version() -> str:
8686
return default_version
8787

8888

89+
def _determine_react_router_version() -> str:
90+
default_version = "7.5.3"
91+
if (version := os.getenv("REACT_ROUTER_VERSION")) and version != default_version:
92+
from reflex.utils import console
93+
94+
console.warn(
95+
f"You have requested react-router@{version} but the supported version is {default_version}, abandon all hope ye who enter here."
96+
)
97+
return version
98+
return default_version
99+
100+
89101
def _determine_react_version() -> str:
90102
default_version = "19.1.0"
91103
if (version := os.getenv("REACT_VERSION")) and version != default_version:
@@ -113,13 +125,15 @@ class Commands(SimpleNamespace):
113125

114126
_react_version = _determine_react_version()
115127

128+
_react_router_version = _determine_react_router_version()
129+
116130
DEPENDENCIES = {
117131
"axios": "1.9.0",
118132
"json5": "2.2.3",
119-
"react-router": "7.5.0",
120-
"react-router-dom": "7.5.0",
133+
"react-router": _react_router_version,
134+
"react-router-dom": _react_router_version,
121135
"react-helmet": "6.1.0",
122-
"@react-router/node": "7.5.0",
136+
"@react-router/node": _react_router_version,
123137
"serve": "14.2.4",
124138
"react": _react_version,
125139
"react-dom": _react_version,
@@ -132,8 +146,8 @@ class Commands(SimpleNamespace):
132146
"autoprefixer": "10.4.21",
133147
"postcss": "8.5.3",
134148
"postcss-import": "16.1.0",
135-
"@react-router/dev": "7.5.0",
136-
"@react-router/fs-routes": "7.5.0",
149+
"@react-router/dev": _react_router_version,
150+
"@react-router/fs-routes": _react_router_version,
137151
"vite-plugin-node-polyfills": "0.23.0",
138152
"vite": "6.2.6",
139153
}

tests/integration/test_client_storage.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,10 +475,10 @@ def set_sub_sub(var: str, value: str):
475475

476476
# navigate to the /foo route
477477
with utils.poll_for_navigation(driver):
478-
driver.get(client_side.frontend_url + "/foo")
478+
driver.get(client_side.frontend_url.removesuffix("/") + "/foo/")
479479

480480
# get new references to all cookie and local storage elements
481-
c1 = driver.find_element(By.ID, "c1")
481+
c1 = client_side.poll_for_result(lambda: driver.find_element(By.ID, "c1"))
482482
c2 = driver.find_element(By.ID, "c2")
483483
c3 = driver.find_element(By.ID, "c3")
484484
c4 = driver.find_element(By.ID, "c4")

0 commit comments

Comments
 (0)