@tanstack/react-router 0.0.1-beta.219 → 0.0.1-beta.220

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.219",
4
+ "version": "0.0.1-beta.220",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -42,7 +42,7 @@
42
42
  "@babel/runtime": "^7.16.7",
43
43
  "tiny-invariant": "^1.3.1",
44
44
  "tiny-warning": "^1.0.3",
45
- "@tanstack/history": "0.0.1-beta.219"
45
+ "@tanstack/history": "0.0.1-beta.220"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "rollup --config rollup.config.js"
package/src/Matches.tsx CHANGED
@@ -8,7 +8,7 @@ import { ResolveRelativePath, ToOptions } from './link'
8
8
  import { AnyRoute, ReactNode, rootRouteId } from './route'
9
9
  import { RouteById, RouteByPath, RouteIds, RoutePaths } from './routeInfo'
10
10
  import { RegisteredRouter } from './router'
11
- import { NoInfer, StrictOrFrom, functionalUpdate } from './utils'
11
+ import { NoInfer, StrictOrFrom } from './utils'
12
12
 
13
13
  export function Matches() {
14
14
  const { routesById, state } = useRouter()
@@ -61,6 +61,7 @@ import {
61
61
  pick,
62
62
  replaceEqualDeep,
63
63
  useStableCallback,
64
+ escapeJSON,
64
65
  } from './utils'
65
66
  import { MatchRouteOptions } from './Matches'
66
67
 
@@ -116,6 +117,8 @@ export type BuildLocationFn<TRouteTree extends AnyRoute> = (
116
117
  opts: BuildNextOptions,
117
118
  ) => ParsedLocation
118
119
 
120
+ export type InjectedHtmlEntry = string | (() => Promise<string> | string)
121
+
119
122
  export type RouterContext<
120
123
  TRouteTree extends AnyRoute,
121
124
  // TDehydrated extends Record<string, any>,
@@ -132,6 +135,13 @@ export type RouterContext<
132
135
  buildLocation: BuildLocationFn<TRouteTree>
133
136
  subscribe: Router<TRouteTree>['subscribe']
134
137
  resetNextScrollRef: React.MutableRefObject<boolean>
138
+ injectedHtmlRef: React.MutableRefObject<InjectedHtmlEntry[]>
139
+ injectHtml: (entry: InjectedHtmlEntry) => void
140
+ dehydrateData: <T>(
141
+ key: any,
142
+ getData: T | (() => Promise<T> | T),
143
+ ) => () => void
144
+ hydrateData: <T>(key: any) => T | undefined
135
145
  }
136
146
 
137
147
  export const routerContext = React.createContext<RouterContext<any>>(null!)
@@ -986,21 +996,24 @@ export function RouterProvider<
986
996
 
987
997
  // Default to reloading the route all the time
988
998
  let shouldReload = true
989
- let shouldReloadDeps =
990
- typeof route.options.shouldReload === 'function'
991
- ? route.options.shouldReload?.(loaderContext)
992
- : !!route.options.shouldReload
993
-
994
- if (typeof shouldReloadDeps === 'object') {
995
- // compare the deps to see if they've changed
996
- shouldReload = !deepEqual(
997
- shouldReloadDeps,
998
- match.shouldReloadDeps,
999
- )
1000
999
 
1001
- match.shouldReloadDeps = shouldReloadDeps
1002
- } else {
1003
- shouldReload = !!shouldReloadDeps
1000
+ if (cause !== 'enter') {
1001
+ let shouldReloadDeps =
1002
+ typeof route.options.shouldReload === 'function'
1003
+ ? route.options.shouldReload?.(loaderContext)
1004
+ : !!(route.options.shouldReload ?? true)
1005
+
1006
+ if (typeof shouldReloadDeps === 'object') {
1007
+ // compare the deps to see if they've changed
1008
+ shouldReload = !deepEqual(
1009
+ shouldReloadDeps,
1010
+ match.shouldReloadDeps,
1011
+ )
1012
+
1013
+ match.shouldReloadDeps = shouldReloadDeps
1014
+ } else {
1015
+ shouldReload = !!shouldReloadDeps
1016
+ }
1004
1017
  }
1005
1018
 
1006
1019
  // If the user doesn't want the route to reload, just
@@ -1377,16 +1390,6 @@ export function RouterProvider<
1377
1390
  }
1378
1391
  }, [history])
1379
1392
 
1380
- React.useLayoutEffect(() => {
1381
- startReactTransition(() => {
1382
- try {
1383
- load()
1384
- } catch (err) {
1385
- console.error(err)
1386
- }
1387
- })
1388
- }, [])
1389
-
1390
1393
  const matchRoute = useStableCallback<MatchRouteFn<TRouteTree>>(
1391
1394
  (location, opts) => {
1392
1395
  location = {
@@ -1429,6 +1432,60 @@ export function RouterProvider<
1429
1432
  },
1430
1433
  )
1431
1434
 
1435
+ const injectedHtmlRef = React.useRef<InjectedHtmlEntry[]>([])
1436
+
1437
+ const injectHtml = useStableCallback(
1438
+ async (html: string | (() => Promise<string> | string)) => {
1439
+ injectedHtmlRef.current.push(html)
1440
+ },
1441
+ )
1442
+
1443
+ const dehydrateData = useStableCallback(
1444
+ <T,>(key: any, getData: T | (() => Promise<T> | T)) => {
1445
+ if (typeof document === 'undefined') {
1446
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key)
1447
+
1448
+ injectHtml(async () => {
1449
+ const id = `__TSR_DEHYDRATED__${strKey}`
1450
+ const data =
1451
+ typeof getData === 'function' ? await (getData as any)() : getData
1452
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
1453
+ strKey,
1454
+ )}"] = ${JSON.stringify(data)}
1455
+ ;(() => {
1456
+ var el = document.getElementById('${id}')
1457
+ el.parentElement.removeChild(el)
1458
+ })()
1459
+ </script>`
1460
+ })
1461
+
1462
+ return () => hydrateData<T>(key)
1463
+ }
1464
+
1465
+ return () => undefined
1466
+ },
1467
+ )
1468
+
1469
+ const hydrateData = useStableCallback(<T extends any = unknown>(key: any) => {
1470
+ if (typeof document !== 'undefined') {
1471
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key)
1472
+
1473
+ return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
1474
+ }
1475
+
1476
+ return undefined
1477
+ })
1478
+
1479
+ React.useLayoutEffect(() => {
1480
+ startReactTransition(() => {
1481
+ try {
1482
+ load()
1483
+ } catch (err) {
1484
+ console.error(err)
1485
+ }
1486
+ })
1487
+ }, [])
1488
+
1432
1489
  const routerContextValue: RouterContext<TRouteTree> = {
1433
1490
  routeTree: router.routeTree,
1434
1491
  navigate,
@@ -1442,6 +1499,10 @@ export function RouterProvider<
1442
1499
  buildLocation,
1443
1500
  subscribe: router.subscribe,
1444
1501
  resetNextScrollRef,
1502
+ injectedHtmlRef,
1503
+ injectHtml,
1504
+ dehydrateData,
1505
+ hydrateData,
1445
1506
  }
1446
1507
 
1447
1508
  return (
package/src/awaited.tsx CHANGED
@@ -1,40 +1,40 @@
1
- // import { DeferredPromise, isDehydratedDeferred } from '@tanstack/react-router'
2
- // import { useRouter } from './react'
3
-
4
- // export type AwaitOptions<T> = {
5
- // promise: DeferredPromise<T>
6
- // }
7
-
8
- // export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
9
- // const router = useRouter()
10
-
11
- // let state = promise.__deferredState
12
- // const key = `__TSR__DEFERRED__${state.uid}`
13
-
14
- // if (isDehydratedDeferred(promise)) {
15
- // state = router.hydrateData(key)!
16
- // promise = Promise.resolve(state.data) as DeferredPromise<any>
17
- // promise.__deferredState = state
18
- // }
19
-
20
- // if (state.status === 'pending') {
21
- // throw promise
22
- // }
23
-
24
- // if (state.status === 'error') {
25
- // throw state.error
26
- // }
27
-
28
- // router.dehydrateData(key, state)
29
-
30
- // return [state.data]
31
- // }
32
-
33
- // export function Await<T>(
34
- // props: AwaitOptions<T> & {
35
- // children: (result: T) => JSX.Element
36
- // },
37
- // ) {
38
- // const awaited = useAwaited(props)
39
- // return props.children(...awaited)
40
- // }
1
+ import { useRouter } from './RouterProvider'
2
+ import { DeferredPromise, isDehydratedDeferred } from './defer'
3
+
4
+ export type AwaitOptions<T> = {
5
+ promise: DeferredPromise<T>
6
+ }
7
+
8
+ export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
9
+ const router = useRouter()
10
+
11
+ let state = promise.__deferredState
12
+ const key = `__TSR__DEFERRED__${state.uid}`
13
+
14
+ if (isDehydratedDeferred(promise)) {
15
+ state = router.hydrateData(key)!
16
+ promise = Promise.resolve(state.data) as DeferredPromise<any>
17
+ promise.__deferredState = state
18
+ }
19
+
20
+ if (state.status === 'pending') {
21
+ throw promise
22
+ }
23
+
24
+ if (state.status === 'error') {
25
+ throw state.error
26
+ }
27
+
28
+ router.dehydrateData(key, state)
29
+
30
+ return [state.data]
31
+ }
32
+
33
+ export function Await<T>(
34
+ props: AwaitOptions<T> & {
35
+ children: (result: T) => JSX.Element
36
+ },
37
+ ) {
38
+ const awaited = useAwaited(props)
39
+ return props.children(...awaited)
40
+ }
package/src/defer.ts CHANGED
@@ -1,55 +1,55 @@
1
- // export type DeferredPromiseState<T> = { uid: string } & (
2
- // | {
3
- // status: 'pending'
4
- // data?: T
5
- // error?: unknown
6
- // }
7
- // | {
8
- // status: 'success'
9
- // data: T
10
- // }
11
- // | {
12
- // status: 'error'
13
- // data?: T
14
- // error: unknown
15
- // }
16
- // )
1
+ export type DeferredPromiseState<T> = { uid: string } & (
2
+ | {
3
+ status: 'pending'
4
+ data?: T
5
+ error?: unknown
6
+ }
7
+ | {
8
+ status: 'success'
9
+ data: T
10
+ }
11
+ | {
12
+ status: 'error'
13
+ data?: T
14
+ error: unknown
15
+ }
16
+ )
17
17
 
18
- // export type DeferredPromise<T> = Promise<T> & {
19
- // __deferredState: DeferredPromiseState<T>
20
- // }
18
+ export type DeferredPromise<T> = Promise<T> & {
19
+ __deferredState: DeferredPromiseState<T>
20
+ }
21
21
 
22
- // export function defer<T>(_promise: Promise<T>) {
23
- // const promise = _promise as DeferredPromise<T>
22
+ export function defer<T>(_promise: Promise<T>) {
23
+ const promise = _promise as DeferredPromise<T>
24
24
 
25
- // if (!promise.__deferredState) {
26
- // promise.__deferredState = {
27
- // uid: Math.random().toString(36).slice(2),
28
- // status: 'pending',
29
- // }
25
+ if (!promise.__deferredState) {
26
+ promise.__deferredState = {
27
+ uid: Math.random().toString(36).slice(2),
28
+ status: 'pending',
29
+ }
30
30
 
31
- // const state = promise.__deferredState
31
+ const state = promise.__deferredState
32
32
 
33
- // promise
34
- // .then((data) => {
35
- // state.status = 'success' as any
36
- // state.data = data
37
- // })
38
- // .catch((error) => {
39
- // state.status = 'error' as any
40
- // state.error = error
41
- // })
42
- // }
33
+ promise
34
+ .then((data) => {
35
+ state.status = 'success' as any
36
+ state.data = data
37
+ })
38
+ .catch((error) => {
39
+ state.status = 'error' as any
40
+ state.error = error
41
+ })
42
+ }
43
43
 
44
- // return promise
45
- // }
44
+ return promise
45
+ }
46
46
 
47
- // export function isDehydratedDeferred(obj: any): boolean {
48
- // return (
49
- // typeof obj === 'object' &&
50
- // obj !== null &&
51
- // !(obj instanceof Promise) &&
52
- // !obj.then &&
53
- // '__deferredState' in obj
54
- // )
55
- // }
47
+ export function isDehydratedDeferred(obj: any): boolean {
48
+ return (
49
+ typeof obj === 'object' &&
50
+ obj !== null &&
51
+ !(obj instanceof Promise) &&
52
+ !obj.then &&
53
+ '__deferredState' in obj
54
+ )
55
+ }
package/src/index.tsx CHANGED
@@ -2,8 +2,8 @@
2
2
  export * from '@tanstack/history'
3
3
  export { default as invariant } from 'tiny-invariant'
4
4
  export { default as warning } from 'tiny-warning'
5
- // export * from './awaited
6
- // export * from './defer'
5
+ export * from './awaited'
6
+ export * from './defer'
7
7
  export * from './CatchBoundary'
8
8
  export * from './fileRoute'
9
9
  export * from './history'
package/src/router.ts CHANGED
@@ -281,49 +281,6 @@ export class Router<
281
281
  // })
282
282
  // }
283
283
 
284
- // TODO:
285
- // injectedHtml: (string | (() => Promise<string> | string))[] = []
286
-
287
- // TODO:
288
- // injectHtml = async (html: string | (() => Promise<string> | string)) => {
289
- // this.injectedHtml.push(html)
290
- // }
291
-
292
- // TODO:
293
- // dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
294
- // if (typeof document === 'undefined') {
295
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
296
-
297
- // this.injectHtml(async () => {
298
- // const id = `__TSR_DEHYDRATED__${strKey}`
299
- // const data =
300
- // typeof getData === 'function' ? await (getData as any)() : getData
301
- // return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
302
- // strKey,
303
- // )}"] = ${JSON.stringify(data)}
304
- // ;(() => {
305
- // var el = document.getElementById('${id}')
306
- // el.parentElement.removeChild(el)
307
- // })()
308
- // </script>`
309
- // })
310
-
311
- // return () => this.hydrateData<T>(key)
312
- // }
313
-
314
- // return () => undefined
315
- // }
316
-
317
- // hydrateData = <T = unknown>(key: any) => {
318
- // if (typeof document !== 'undefined') {
319
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
320
-
321
- // return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
322
- // }
323
-
324
- // return undefined
325
- // }
326
-
327
284
  // resolveMatchPromise = (matchId: string, key: string, value: any) => {
328
285
  // state.matches
329
286
  // .find((d) => d.id === matchId)
@@ -359,13 +316,6 @@ export class Router<
359
316
  // }
360
317
  }
361
318
 
362
- function escapeJSON(jsonString: string) {
363
- return jsonString
364
- .replace(/\\/g, '\\\\') // Escape backslashes
365
- .replace(/'/g, "\\'") // Escape single quotes
366
- .replace(/"/g, '\\"') // Escape double quotes
367
- }
368
-
369
319
  // A function that takes an import() argument which is a function and returns a new function that will
370
320
  // proxy arguments from the caller to the imported function, retaining all type
371
321
  // information along the way
package/src/utils.ts CHANGED
@@ -341,3 +341,10 @@ export function useRouteContext<
341
341
 
342
342
  export const useLayoutEffect =
343
343
  typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect
344
+
345
+ export function escapeJSON(jsonString: string) {
346
+ return jsonString
347
+ .replace(/\\/g, '\\\\') // Escape backslashes
348
+ .replace(/'/g, "\\'") // Escape single quotes
349
+ .replace(/"/g, '\\"') // Escape double quotes
350
+ }