reflex 0.7.14a6__py3-none-any.whl → 0.8.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (206) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -6
  7. reflex/.templates/web/app/entry.client.js +8 -0
  8. reflex/.templates/web/app/routes.js +10 -0
  9. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -32
  10. reflex/.templates/web/postcss.config.js +1 -1
  11. reflex/.templates/web/react-router.config.js +6 -0
  12. reflex/.templates/web/utils/client_side_routing.js +21 -19
  13. reflex/.templates/web/utils/react-theme.js +89 -0
  14. reflex/.templates/web/utils/state.js +155 -67
  15. reflex/.templates/web/vite.config.js +32 -0
  16. reflex/__init__.py +1 -6
  17. reflex/__init__.pyi +0 -4
  18. reflex/app.py +52 -115
  19. reflex/base.py +1 -87
  20. reflex/compiler/compiler.py +40 -3
  21. reflex/compiler/utils.py +54 -28
  22. reflex/components/__init__.py +0 -2
  23. reflex/components/__init__.pyi +0 -3
  24. reflex/components/base/__init__.py +1 -5
  25. reflex/components/base/__init__.pyi +4 -6
  26. reflex/components/base/app_wrap.pyi +5 -4
  27. reflex/components/base/body.pyi +5 -4
  28. reflex/components/base/document.py +18 -14
  29. reflex/components/base/document.pyi +83 -27
  30. reflex/components/base/error_boundary.pyi +5 -4
  31. reflex/components/base/fragment.pyi +5 -4
  32. reflex/components/base/link.pyi +9 -7
  33. reflex/components/base/meta.pyi +17 -13
  34. reflex/components/base/script.py +60 -58
  35. reflex/components/base/script.pyi +246 -31
  36. reflex/components/base/strict_mode.pyi +5 -4
  37. reflex/components/component.py +109 -194
  38. reflex/components/core/__init__.py +1 -0
  39. reflex/components/core/__init__.pyi +1 -0
  40. reflex/components/core/auto_scroll.pyi +5 -4
  41. reflex/components/core/banner.pyi +25 -19
  42. reflex/components/core/client_side_routing.py +7 -6
  43. reflex/components/core/client_side_routing.pyi +6 -56
  44. reflex/components/core/clipboard.pyi +5 -4
  45. reflex/components/core/debounce.py +1 -0
  46. reflex/components/core/debounce.pyi +5 -4
  47. reflex/components/core/foreach.py +3 -2
  48. reflex/components/core/helmet.py +14 -0
  49. reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
  50. reflex/components/core/html.pyi +5 -4
  51. reflex/components/core/sticky.pyi +17 -13
  52. reflex/components/core/upload.py +2 -1
  53. reflex/components/core/upload.pyi +21 -16
  54. reflex/components/datadisplay/code.pyi +9 -7
  55. reflex/components/datadisplay/dataeditor.pyi +5 -4
  56. reflex/components/datadisplay/shiki_code_block.pyi +13 -10
  57. reflex/components/dynamic.py +4 -5
  58. reflex/components/el/element.pyi +5 -4
  59. reflex/components/el/elements/base.pyi +5 -4
  60. reflex/components/el/elements/forms.pyi +69 -52
  61. reflex/components/el/elements/inline.pyi +113 -85
  62. reflex/components/el/elements/media.pyi +105 -79
  63. reflex/components/el/elements/metadata.pyi +25 -19
  64. reflex/components/el/elements/other.pyi +29 -22
  65. reflex/components/el/elements/scripts.pyi +13 -10
  66. reflex/components/el/elements/sectioning.pyi +61 -46
  67. reflex/components/el/elements/tables.pyi +41 -31
  68. reflex/components/el/elements/typography.pyi +61 -46
  69. reflex/components/field.py +175 -0
  70. reflex/components/gridjs/datatable.py +2 -2
  71. reflex/components/gridjs/datatable.pyi +11 -9
  72. reflex/components/lucide/icon.py +6 -2
  73. reflex/components/lucide/icon.pyi +15 -10
  74. reflex/components/markdown/markdown.pyi +5 -4
  75. reflex/components/moment/moment.pyi +5 -4
  76. reflex/components/plotly/plotly.pyi +19 -10
  77. reflex/components/props.py +376 -27
  78. reflex/components/radix/primitives/accordion.py +8 -1
  79. reflex/components/radix/primitives/accordion.pyi +29 -22
  80. reflex/components/radix/primitives/base.pyi +9 -7
  81. reflex/components/radix/primitives/drawer.pyi +45 -34
  82. reflex/components/radix/primitives/form.pyi +41 -31
  83. reflex/components/radix/primitives/progress.pyi +21 -16
  84. reflex/components/radix/primitives/slider.pyi +21 -16
  85. reflex/components/radix/themes/base.py +3 -3
  86. reflex/components/radix/themes/base.pyi +33 -25
  87. reflex/components/radix/themes/color_mode.pyi +13 -10
  88. reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
  89. reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
  90. reflex/components/radix/themes/components/avatar.pyi +5 -4
  91. reflex/components/radix/themes/components/badge.pyi +5 -4
  92. reflex/components/radix/themes/components/button.pyi +5 -4
  93. reflex/components/radix/themes/components/callout.pyi +21 -16
  94. reflex/components/radix/themes/components/card.pyi +5 -4
  95. reflex/components/radix/themes/components/checkbox.pyi +13 -10
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
  97. reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
  98. reflex/components/radix/themes/components/context_menu.pyi +53 -40
  99. reflex/components/radix/themes/components/data_list.pyi +17 -13
  100. reflex/components/radix/themes/components/dialog.pyi +29 -22
  101. reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
  102. reflex/components/radix/themes/components/hover_card.pyi +17 -13
  103. reflex/components/radix/themes/components/icon_button.pyi +5 -4
  104. reflex/components/radix/themes/components/inset.pyi +5 -4
  105. reflex/components/radix/themes/components/popover.pyi +17 -13
  106. reflex/components/radix/themes/components/progress.pyi +5 -4
  107. reflex/components/radix/themes/components/radio.pyi +5 -4
  108. reflex/components/radix/themes/components/radio_cards.pyi +9 -7
  109. reflex/components/radix/themes/components/radio_group.pyi +17 -13
  110. reflex/components/radix/themes/components/scroll_area.pyi +5 -4
  111. reflex/components/radix/themes/components/segmented_control.pyi +9 -7
  112. reflex/components/radix/themes/components/select.pyi +37 -28
  113. reflex/components/radix/themes/components/separator.pyi +5 -4
  114. reflex/components/radix/themes/components/skeleton.pyi +5 -4
  115. reflex/components/radix/themes/components/slider.pyi +5 -4
  116. reflex/components/radix/themes/components/spinner.pyi +5 -4
  117. reflex/components/radix/themes/components/switch.pyi +5 -4
  118. reflex/components/radix/themes/components/table.pyi +29 -22
  119. reflex/components/radix/themes/components/tabs.pyi +21 -16
  120. reflex/components/radix/themes/components/text_area.pyi +5 -4
  121. reflex/components/radix/themes/components/text_field.pyi +13 -10
  122. reflex/components/radix/themes/components/tooltip.pyi +5 -4
  123. reflex/components/radix/themes/layout/base.pyi +5 -4
  124. reflex/components/radix/themes/layout/box.pyi +5 -4
  125. reflex/components/radix/themes/layout/center.pyi +5 -4
  126. reflex/components/radix/themes/layout/container.pyi +5 -4
  127. reflex/components/radix/themes/layout/flex.pyi +5 -4
  128. reflex/components/radix/themes/layout/grid.pyi +5 -4
  129. reflex/components/radix/themes/layout/list.pyi +21 -16
  130. reflex/components/radix/themes/layout/section.pyi +5 -4
  131. reflex/components/radix/themes/layout/spacer.pyi +5 -4
  132. reflex/components/radix/themes/layout/stack.pyi +13 -10
  133. reflex/components/radix/themes/typography/blockquote.pyi +5 -4
  134. reflex/components/radix/themes/typography/code.pyi +5 -4
  135. reflex/components/radix/themes/typography/heading.pyi +5 -4
  136. reflex/components/radix/themes/typography/link.py +42 -9
  137. reflex/components/radix/themes/typography/link.pyi +311 -6
  138. reflex/components/radix/themes/typography/text.pyi +29 -22
  139. reflex/components/react_player/audio.pyi +5 -4
  140. reflex/components/react_player/react_player.pyi +5 -4
  141. reflex/components/react_player/video.pyi +5 -4
  142. reflex/components/recharts/cartesian.py +2 -1
  143. reflex/components/recharts/cartesian.pyi +65 -46
  144. reflex/components/recharts/charts.py +4 -2
  145. reflex/components/recharts/charts.pyi +36 -24
  146. reflex/components/recharts/general.pyi +24 -18
  147. reflex/components/recharts/polar.py +8 -4
  148. reflex/components/recharts/polar.pyi +16 -10
  149. reflex/components/recharts/recharts.pyi +9 -7
  150. reflex/components/sonner/toast.py +2 -2
  151. reflex/components/sonner/toast.pyi +7 -6
  152. reflex/config.py +3 -77
  153. reflex/constants/__init__.py +1 -0
  154. reflex/constants/base.py +38 -8
  155. reflex/constants/compiler.py +4 -2
  156. reflex/constants/event.py +1 -0
  157. reflex/constants/installer.py +23 -16
  158. reflex/constants/state.py +2 -0
  159. reflex/custom_components/custom_components.py +0 -14
  160. reflex/environment.py +1 -1
  161. reflex/event.py +178 -81
  162. reflex/experimental/__init__.py +0 -30
  163. reflex/istate/proxy.py +5 -3
  164. reflex/page.py +0 -27
  165. reflex/plugins/__init__.py +3 -2
  166. reflex/plugins/base.py +5 -1
  167. reflex/plugins/shared_tailwind.py +158 -0
  168. reflex/plugins/sitemap.py +206 -0
  169. reflex/plugins/tailwind_v3.py +13 -106
  170. reflex/plugins/tailwind_v4.py +15 -108
  171. reflex/reflex.py +1 -0
  172. reflex/state.py +134 -140
  173. reflex/testing.py +57 -9
  174. reflex/utils/build.py +9 -69
  175. reflex/utils/exec.py +59 -161
  176. reflex/utils/export.py +1 -1
  177. reflex/utils/imports.py +0 -4
  178. reflex/utils/misc.py +28 -0
  179. reflex/utils/prerequisites.py +62 -59
  180. reflex/utils/processes.py +6 -6
  181. reflex/utils/pyi_generator.py +21 -9
  182. reflex/utils/serializers.py +14 -1
  183. reflex/utils/types.py +196 -61
  184. reflex/vars/__init__.py +2 -0
  185. reflex/vars/base.py +367 -134
  186. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
  187. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -197
  188. reflex/.templates/web/next.config.js +0 -7
  189. reflex/components/base/head.py +0 -20
  190. reflex/components/base/head.pyi +0 -116
  191. reflex/components/next/__init__.py +0 -10
  192. reflex/components/next/base.py +0 -7
  193. reflex/components/next/image.py +0 -117
  194. reflex/components/next/image.pyi +0 -94
  195. reflex/components/next/link.py +0 -20
  196. reflex/components/next/link.pyi +0 -67
  197. reflex/components/next/video.py +0 -38
  198. reflex/components/next/video.pyi +0 -68
  199. reflex/components/suneditor/__init__.py +0 -5
  200. reflex/components/suneditor/editor.py +0 -269
  201. reflex/components/suneditor/editor.pyi +0 -199
  202. reflex/experimental/layout.py +0 -254
  203. reflex/experimental/layout.pyi +0 -814
  204. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
  205. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
  206. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -6,14 +6,19 @@ import env from "$/env.json";
6
6
  import reflexEnvironment from "$/reflex.json";
7
7
  import Cookies from "universal-cookie";
8
8
  import { useEffect, useRef, useState } from "react";
9
- import Router, { useRouter } from "next/router";
9
+ import {
10
+ useLocation,
11
+ useNavigate,
12
+ useSearchParams,
13
+ useParams,
14
+ } from "react-router";
10
15
  import {
11
16
  initialEvents,
12
17
  initialState,
13
18
  onLoadInternalEvent,
14
19
  state_name,
15
20
  exception_state_name,
16
- } from "$/utils/context.js";
21
+ } from "$/utils/context";
17
22
  import debounce from "$/utils/helpers/debounce";
18
23
  import throttle from "$/utils/helpers/throttle";
19
24
 
@@ -158,24 +163,48 @@ export const evalReactComponent = async (component) => {
158
163
  * Only Queue and process events when websocket connection exists.
159
164
  * @param event The event to queue.
160
165
  * @param socket The socket object to send the event on.
166
+ * @param navigate The navigate function from React Router
167
+ * @param params The params object from React Router
161
168
  *
162
169
  * @returns Adds event to queue and processes it if websocket exits, does nothing otherwise.
163
170
  */
164
- export const queueEventIfSocketExists = async (events, socket) => {
171
+ export const queueEventIfSocketExists = async (
172
+ events,
173
+ socket,
174
+ navigate,
175
+ params,
176
+ ) => {
165
177
  if (!socket) {
166
178
  return;
167
179
  }
168
- await queueEvents(events, socket);
180
+ await queueEvents(events, socket, navigate, params);
169
181
  };
170
182
 
183
+ /**
184
+ * Check if a string is a valid HTTP URL.
185
+ * @param string The string to check.
186
+ *
187
+ * @returns The URL object if valid, undefined otherwise.
188
+ * */
189
+ function urlFrom(string) {
190
+ try {
191
+ return new URL(string);
192
+ } catch {
193
+ return undefined;
194
+ }
195
+ return undefined;
196
+ }
197
+
171
198
  /**
172
199
  * Handle frontend event or send the event to the backend via Websocket.
173
200
  * @param event The event to send.
174
201
  * @param socket The socket object to send the event on.
202
+ * @param navigate The navigate function from useNavigate
203
+ * @param params The params object from useParams
175
204
  *
176
205
  * @returns True if the event was sent, false if it was handled locally.
177
206
  */
178
- export const applyEvent = async (event, socket) => {
207
+ export const applyEvent = async (event, socket, navigate, params) => {
179
208
  // Handle special events
180
209
  if (event.name == "_redirect") {
181
210
  if ((event.payload.path ?? undefined) === undefined) {
@@ -183,41 +212,54 @@ export const applyEvent = async (event, socket) => {
183
212
  }
184
213
  if (event.payload.external) {
185
214
  window.open(event.payload.path, "_blank", "noopener");
186
- } else if (event.payload.replace) {
187
- Router.replace(event.payload.path);
215
+ return false;
216
+ }
217
+ const url = urlFrom(event.payload.path);
218
+ let pathname = event.payload.path;
219
+ if (url) {
220
+ if (url.host !== window.location.host) {
221
+ // External URL
222
+ window.location.assign(event.payload.path);
223
+ return false;
224
+ } else {
225
+ pathname = url.pathname + url.search + url.hash;
226
+ }
227
+ }
228
+ if (event.payload.replace) {
229
+ navigate(pathname, { replace: true });
188
230
  } else {
189
- Router.push(event.payload.path);
231
+ navigate(pathname);
190
232
  }
191
233
  return false;
192
234
  }
193
235
 
194
236
  if (event.name == "_remove_cookie") {
195
237
  cookies.remove(event.payload.key, { ...event.payload.options });
196
- queueEventIfSocketExists(initialEvents(), socket);
238
+ queueEventIfSocketExists(initialEvents(), socket, navigate, params);
197
239
  return false;
198
240
  }
199
241
 
200
242
  if (event.name == "_clear_local_storage") {
201
243
  localStorage.clear();
202
- queueEventIfSocketExists(initialEvents(), socket);
244
+ queueEventIfSocketExists(initialEvents(), socket, navigate, params);
203
245
  return false;
204
246
  }
205
247
 
206
248
  if (event.name == "_remove_local_storage") {
207
249
  localStorage.removeItem(event.payload.key);
208
- queueEventIfSocketExists(initialEvents(), socket);
250
+ queueEventIfSocketExists(initialEvents(), socket, navigate, params);
209
251
  return false;
210
252
  }
211
253
 
212
254
  if (event.name == "_clear_session_storage") {
213
255
  sessionStorage.clear();
214
- queueEvents(initialEvents(), socket);
256
+ queueEvents(initialEvents(), socket, navigate, params);
215
257
  return false;
216
258
  }
217
259
 
218
260
  if (event.name == "_remove_session_storage") {
219
261
  sessionStorage.removeItem(event.payload.key);
220
- queueEvents(initialEvents(), socket);
262
+ queueEvents(initialEvents(), socket, navigate, params);
221
263
  return false;
222
264
  }
223
265
 
@@ -322,11 +364,15 @@ export const applyEvent = async (event, socket) => {
322
364
  event.router_data === undefined ||
323
365
  Object.keys(event.router_data).length === 0
324
366
  ) {
325
- event.router_data = (({ pathname, query, asPath }) => ({
326
- pathname,
327
- query,
328
- asPath,
329
- }))(Router);
367
+ // Since we don't have router directly, we need to get info from our hooks
368
+ event.router_data = {
369
+ pathname: window.location.pathname,
370
+ query: {
371
+ ...Object.fromEntries(new URLSearchParams(window.location.search)),
372
+ ...params(),
373
+ },
374
+ asPath: window.location.pathname + window.location.search,
375
+ };
330
376
  }
331
377
 
332
378
  // Send the event to the server.
@@ -342,15 +388,22 @@ export const applyEvent = async (event, socket) => {
342
388
  * Send an event to the server via REST.
343
389
  * @param event The current event.
344
390
  * @param socket The socket object to send the response event(s) on.
391
+ * @param navigate The navigate function from React Router
392
+ * @param params The params object from React Router
345
393
  *
346
394
  * @returns Whether the event was sent.
347
395
  */
348
- export const applyRestEvent = async (event, socket) => {
396
+ export const applyRestEvent = async (event, socket, navigate, params) => {
349
397
  let eventSent = false;
350
398
  if (event.handler === "uploadFiles") {
351
399
  if (event.payload.files === undefined || event.payload.files.length === 0) {
352
400
  // Submit the event over the websocket to trigger the event handler.
353
- return await applyEvent(Event(event.name, { files: [] }), socket);
401
+ return await applyEvent(
402
+ Event(event.name, { files: [] }),
403
+ socket,
404
+ navigate,
405
+ params,
406
+ );
354
407
  }
355
408
 
356
409
  // Start upload, but do not wait for it, which would block other events.
@@ -371,8 +424,16 @@ export const applyRestEvent = async (event, socket) => {
371
424
  * @param events Array of events to queue.
372
425
  * @param socket The socket object to send the event on.
373
426
  * @param prepend Whether to place the events at the beginning of the queue.
427
+ * @param navigate The navigate function from React Router
428
+ * @param params The params object from React Router
374
429
  */
375
- export const queueEvents = async (events, socket, prepend) => {
430
+ export const queueEvents = async (
431
+ events,
432
+ socket,
433
+ prepend,
434
+ navigate,
435
+ params,
436
+ ) => {
376
437
  if (prepend) {
377
438
  // Drain the existing queue and place it after the given events.
378
439
  events = [
@@ -383,14 +444,16 @@ export const queueEvents = async (events, socket, prepend) => {
383
444
  ];
384
445
  }
385
446
  event_queue.push(...events.filter((e) => e !== undefined && e !== null));
386
- await processEvent(socket.current);
447
+ await processEvent(socket.current, navigate, params);
387
448
  };
388
449
 
389
450
  /**
390
451
  * Process an event off the event queue.
391
452
  * @param socket The socket object to send the event on.
453
+ * @param navigate The navigate function from React Router
454
+ * @param params The params object from React Router
392
455
  */
393
- export const processEvent = async (socket) => {
456
+ export const processEvent = async (socket, navigate, params) => {
394
457
  // Only proceed if the socket is up and no event in the queue uses state, otherwise we throw the event into the void
395
458
  if (!socket && isStateful()) {
396
459
  return;
@@ -410,16 +473,16 @@ export const processEvent = async (socket) => {
410
473
  let eventSent = false;
411
474
  // Process events with handlers via REST and all others via websockets.
412
475
  if (event.handler) {
413
- eventSent = await applyRestEvent(event, socket);
476
+ eventSent = await applyRestEvent(event, socket, navigate, params);
414
477
  } else {
415
- eventSent = await applyEvent(event, socket);
478
+ eventSent = await applyEvent(event, socket, navigate, params);
416
479
  }
417
480
  // If no event was sent, set processing to false.
418
481
  if (!eventSent) {
419
482
  event_processing = false;
420
483
  // recursively call processEvent to drain the queue, since there is
421
484
  // no state update to trigger the useEffect event loop.
422
- await processEvent(socket);
485
+ await processEvent(socket, navigate, params);
423
486
  }
424
487
  };
425
488
 
@@ -430,6 +493,8 @@ export const processEvent = async (socket) => {
430
493
  * @param transports The transports to use.
431
494
  * @param setConnectErrors The function to update connection error value.
432
495
  * @param client_storage The client storage object from context.js
496
+ * @param navigate The navigate function from React Router
497
+ * @param params The params object from React Router
433
498
  */
434
499
  export const connect = async (
435
500
  socket,
@@ -437,6 +502,8 @@ export const connect = async (
437
502
  transports,
438
503
  setConnectErrors,
439
504
  client_storage = {},
505
+ navigate,
506
+ params,
440
507
  ) => {
441
508
  // Get backend URL object from the endpoint.
442
509
  const endpoint = getBackendURL(EVENTURL);
@@ -511,12 +578,12 @@ export const connect = async (
511
578
  applyClientStorageDelta(client_storage, update.delta);
512
579
  event_processing = !update.final;
513
580
  if (update.events) {
514
- queueEvents(update.events, socket);
581
+ queueEvents(update.events, socket, false, navigate, params);
515
582
  }
516
583
  });
517
584
  socket.current.on("reload", async (event) => {
518
585
  event_processing = false;
519
- queueEvents([...initialEvents(), event], socket, true);
586
+ queueEvents([...initialEvents(), event], socket, true, navigate, params);
520
587
  });
521
588
 
522
589
  document.addEventListener("visibilitychange", checkVisibility);
@@ -710,7 +777,10 @@ const applyClientStorageDelta = (client_storage, delta) => {
710
777
  );
711
778
  if (unqualified_states.length === 1) {
712
779
  const main_state = delta[unqualified_states[0]];
713
- if (main_state.is_hydrated !== undefined && !main_state.is_hydrated) {
780
+ if (
781
+ main_state.is_hydrated_rx_state_ !== undefined &&
782
+ !main_state.is_hydrated_rx_state_
783
+ ) {
714
784
  // skip if the state is not hydrated yet, since all client storage
715
785
  // values are sent in the hydrate event
716
786
  return;
@@ -748,7 +818,7 @@ const applyClientStorageDelta = (client_storage, delta) => {
748
818
  };
749
819
 
750
820
  /**
751
- * Establish websocket event loop for a NextJS page.
821
+ * Establish websocket event loop for a React Router page.
752
822
  * @param dispatch The reducer dispatch function to update state.
753
823
  * @param initial_events The initial app events.
754
824
  * @param client_storage The client storage object from context.js
@@ -763,8 +833,17 @@ export const useEventLoop = (
763
833
  client_storage = {},
764
834
  ) => {
765
835
  const socket = useRef(null);
766
- const router = useRouter();
836
+ const location = useLocation();
837
+ const navigate = useNavigate();
838
+ const paramsR = useParams();
839
+ const prevLocationRef = useRef(location);
840
+ const [searchParams] = useSearchParams();
767
841
  const [connectErrors, setConnectErrors] = useState([]);
842
+ const params = useRef(paramsR);
843
+
844
+ useEffect(() => {
845
+ params.current = paramsR;
846
+ }, [paramsR]);
768
847
 
769
848
  // Function to add new events to the event queue.
770
849
  const addEvents = (events, args, event_actions) => {
@@ -803,32 +882,38 @@ export const useEventLoop = (
803
882
  // If debounce is used, queue the events after some delay
804
883
  debounce(
805
884
  combined_name,
806
- () => queueEvents(_events, socket),
885
+ () =>
886
+ queueEvents(_events, socket, false, navigate, () => params.current),
807
887
  event_actions.debounce,
808
888
  );
809
889
  } else {
810
- queueEvents(_events, socket);
890
+ queueEvents(_events, socket, false, navigate, () => params.current);
811
891
  }
812
892
  };
813
893
 
814
894
  const sentHydrate = useRef(false); // Avoid double-hydrate due to React strict-mode
815
895
  useEffect(() => {
816
- if (router.isReady && !sentHydrate.current) {
896
+ if (!sentHydrate.current) {
817
897
  queueEvents(
818
898
  initial_events().map((e) => ({
819
899
  ...e,
820
- router_data: (({ pathname, query, asPath }) => ({
821
- pathname,
822
- query,
823
- asPath,
824
- }))(router),
900
+ router_data: {
901
+ pathname: location.pathname,
902
+ query: {
903
+ ...Object.fromEntries(searchParams.entries()),
904
+ ...params.current,
905
+ },
906
+ asPath: location.pathname + location.search,
907
+ },
825
908
  })),
826
909
  socket,
827
910
  true,
911
+ navigate,
912
+ () => params.current,
828
913
  );
829
914
  sentHydrate.current = true;
830
915
  }
831
- }, [router.isReady]);
916
+ }, []);
832
917
 
833
918
  // Handle frontend errors and send them to the backend via websocket.
834
919
  useEffect(() => {
@@ -871,6 +956,8 @@ export const useEventLoop = (
871
956
  ["websocket"],
872
957
  setConnectErrors,
873
958
  client_storage,
959
+ navigate,
960
+ () => params.current,
874
961
  );
875
962
  }
876
963
  }
@@ -885,14 +972,14 @@ export const useEventLoop = (
885
972
 
886
973
  // Main event loop.
887
974
  useEffect(() => {
888
- // Skip if the router is not ready.
889
- if (!router.isReady || isBackendDisabled()) {
975
+ // Skip if the backend is disabled
976
+ if (isBackendDisabled()) {
890
977
  return;
891
978
  }
892
979
  (async () => {
893
980
  // Process all outstanding events.
894
981
  while (event_queue.length > 0 && !event_processing) {
895
- await processEvent(socket.current);
982
+ await processEvent(socket.current, navigate, () => params.current);
896
983
  }
897
984
  })();
898
985
  });
@@ -928,31 +1015,32 @@ export const useEventLoop = (
928
1015
  return () => window.removeEventListener("storage", handleStorage);
929
1016
  });
930
1017
 
931
- // Route after the initial page hydration.
1018
+ // Route after the initial page hydration
932
1019
  useEffect(() => {
933
- const change_start = () => {
934
- const main_state_dispatch = dispatch["reflex___state____state"];
935
- if (main_state_dispatch !== undefined) {
936
- main_state_dispatch({ is_hydrated: false });
937
- }
938
- };
939
- const change_complete = () => addEvents(onLoadInternalEvent());
940
- const change_error = () => {
941
- // Remove cached error state from router for this page, otherwise the
942
- // page will never send on_load events again.
943
- if (router.components[router.pathname].error) {
944
- delete router.components[router.pathname].error;
945
- }
946
- };
947
- router.events.on("routeChangeStart", change_start);
948
- router.events.on("routeChangeComplete", change_complete);
949
- router.events.on("routeChangeError", change_error);
950
- return () => {
951
- router.events.off("routeChangeStart", change_start);
952
- router.events.off("routeChangeComplete", change_complete);
953
- router.events.off("routeChangeError", change_error);
954
- };
955
- }, [router]);
1020
+ // This will run when the location changes
1021
+ if (
1022
+ location.pathname + location.search + location.hash !==
1023
+ prevLocationRef.current.pathname +
1024
+ prevLocationRef.current.search +
1025
+ prevLocationRef.current.hash
1026
+ ) {
1027
+ // Equivalent to routeChangeStart - runs when navigation begins
1028
+ const change_start = () => {
1029
+ const main_state_dispatch = dispatch["reflex___state____state"];
1030
+ if (main_state_dispatch !== undefined) {
1031
+ main_state_dispatch({ is_hydrated_rx_state_: false });
1032
+ }
1033
+ };
1034
+ change_start();
1035
+
1036
+ // Equivalent to routeChangeComplete - runs after navigation completes
1037
+ const change_complete = () => addEvents(onLoadInternalEvent());
1038
+ change_complete();
1039
+
1040
+ // Update the ref
1041
+ prevLocationRef.current = location;
1042
+ }
1043
+ }, [location, dispatch, onLoadInternalEvent, addEvents]);
956
1044
 
957
1045
  return [addEvents, connectErrors];
958
1046
  };
@@ -0,0 +1,32 @@
1
+ import { fileURLToPath, URL } from "url";
2
+ import { reactRouter } from "@react-router/dev/vite";
3
+ import { defineConfig } from "vite";
4
+
5
+ export default defineConfig((config) => ({
6
+ plugins: [reactRouter()],
7
+ build: {
8
+ rollupOptions: {
9
+ jsx: {},
10
+ },
11
+ },
12
+ server: {
13
+ port: process.env.PORT,
14
+ },
15
+ resolve: {
16
+ mainFields: ["browser", "module", "jsnext"],
17
+ alias: [
18
+ {
19
+ find: "$",
20
+ replacement: fileURLToPath(new URL("./", import.meta.url)),
21
+ },
22
+ {
23
+ find: "@",
24
+ replacement: fileURLToPath(new URL("./public", import.meta.url)),
25
+ },
26
+ ].concat(
27
+ config.command === "build"
28
+ ? [{ find: "react-dom/server", replacement: "react-dom/server.node" }]
29
+ : [],
30
+ ),
31
+ },
32
+ }));
reflex/__init__.py CHANGED
@@ -279,12 +279,7 @@ _MAPPING: dict = {
279
279
  "components.el.elements.media": ["image"],
280
280
  "components.lucide": ["icon"],
281
281
  **COMPONENTS_BASE_MAPPING,
282
- "components.suneditor": [
283
- "editor",
284
- "EditorButtonList",
285
- "EditorOptions",
286
- ],
287
- "components": ["el", "radix", "lucide", "recharts", "next"],
282
+ "components": ["el", "radix", "lucide", "recharts"],
288
283
  "components.markdown": ["markdown"],
289
284
  **RADIX_MAPPING,
290
285
  "components.plotly": ["plotly"],
reflex/__init__.pyi CHANGED
@@ -24,7 +24,6 @@ from .assets import asset as asset
24
24
  from .base import Base as Base
25
25
  from .components import el as el
26
26
  from .components import lucide as lucide
27
- from .components import next as next
28
27
  from .components import radix as radix
29
28
  from .components import recharts as recharts
30
29
  from .components.base.fragment import Fragment as Fragment
@@ -149,9 +148,6 @@ from .components.radix.themes.typography.text import text as text
149
148
  from .components.react_player import audio as audio
150
149
  from .components.react_player import video as video
151
150
  from .components.sonner.toast import toast as toast
152
- from .components.suneditor import EditorButtonList as EditorButtonList
153
- from .components.suneditor import EditorOptions as EditorOptions
154
- from .components.suneditor import editor as editor
155
151
  from .config import Config as Config
156
152
  from .config import DBConfig as DBConfig
157
153
  from .constants import Env as Env