@tanstack/react-router 0.0.1-beta.48 → 0.0.1-beta.49
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/build/cjs/index.js +121 -172
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/useStore.js +64 -0
- package/build/cjs/useStore.js.map +1 -0
- package/build/esm/index.js +167 -165
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +76 -102
- package/build/types/index.d.ts +21 -16
- package/build/umd/index.development.js +1542 -2549
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +3 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +3 -3
- package/src/index.tsx +173 -200
- package/src/useStore.ts +70 -0
- package/src/uSES/useSyncExternalStore.ts +0 -16
- package/src/uSES/useSyncExternalStoreShim.ts +0 -20
- package/src/uSES/useSyncExternalStoreShimClient.ts +0 -87
- package/src/uSES/useSyncExternalStoreShimServer.ts +0 -20
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.
|
|
4
|
+
"version": "0.0.1-beta.49",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router/",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@babel/runtime": "^7.16.7",
|
|
44
|
-
"@solidjs/reactivity": "^0.0.
|
|
44
|
+
"@solidjs/reactivity": "^0.0.7",
|
|
45
45
|
"use-sync-external-store": "^1.2.0",
|
|
46
|
-
"@tanstack/router-core": "0.0.1-beta.
|
|
46
|
+
"@tanstack/router-core": "0.0.1-beta.49"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/use-sync-external-store": "^0.0.3",
|
package/src/index.tsx
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
|
|
3
|
-
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
4
|
-
// @ts-ignore
|
|
5
|
-
// import { useSyncExternalStore } from './uSES/useSyncExternalStoreShim'
|
|
6
|
-
import { createEffect, createRoot, untrack, unwrap } from '@solidjs/reactivity'
|
|
7
|
-
import { createStore } from '@solidjs/reactivity'
|
|
8
|
-
|
|
9
3
|
import {
|
|
10
4
|
Route,
|
|
11
5
|
RegisteredAllRouteInfo,
|
|
12
6
|
RegisteredRouter,
|
|
13
7
|
RouterStore,
|
|
14
8
|
last,
|
|
15
|
-
sharedClone,
|
|
16
|
-
Action,
|
|
17
9
|
warning,
|
|
18
10
|
RouterOptions,
|
|
19
11
|
RouteMatch,
|
|
@@ -23,7 +15,6 @@ import {
|
|
|
23
15
|
AnyAllRouteInfo,
|
|
24
16
|
DefaultAllRouteInfo,
|
|
25
17
|
functionalUpdate,
|
|
26
|
-
createRouter,
|
|
27
18
|
AllRouteInfo,
|
|
28
19
|
ValidFromPath,
|
|
29
20
|
LinkOptions,
|
|
@@ -34,11 +25,19 @@ import {
|
|
|
34
25
|
invariant,
|
|
35
26
|
Router,
|
|
36
27
|
Expand,
|
|
28
|
+
Action,
|
|
29
|
+
ActionStore,
|
|
30
|
+
ActionSubmission,
|
|
37
31
|
} from '@tanstack/router-core'
|
|
32
|
+
import { useStore } from './useStore'
|
|
33
|
+
|
|
34
|
+
//
|
|
38
35
|
|
|
39
36
|
export * from '@tanstack/router-core'
|
|
40
37
|
|
|
41
|
-
export
|
|
38
|
+
export { useStore }
|
|
39
|
+
|
|
40
|
+
//
|
|
42
41
|
|
|
43
42
|
type ReactNode = any
|
|
44
43
|
|
|
@@ -158,7 +157,7 @@ export function useLinkProps<
|
|
|
158
157
|
hash,
|
|
159
158
|
search,
|
|
160
159
|
params,
|
|
161
|
-
to,
|
|
160
|
+
to = '.',
|
|
162
161
|
preload,
|
|
163
162
|
preloadDelay,
|
|
164
163
|
preloadMaxAge,
|
|
@@ -208,7 +207,7 @@ export function useLinkProps<
|
|
|
208
207
|
|
|
209
208
|
// Get the active props
|
|
210
209
|
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
|
211
|
-
? functionalUpdate(activeProps, {}) ?? {}
|
|
210
|
+
? functionalUpdate(activeProps as any, {}) ?? {}
|
|
212
211
|
: {}
|
|
213
212
|
|
|
214
213
|
// Get the inactive props
|
|
@@ -291,89 +290,23 @@ export type MatchesProviderProps = {
|
|
|
291
290
|
children: ReactNode
|
|
292
291
|
}
|
|
293
292
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
export const __useStoreValue = <TSeed, TReturn>(
|
|
297
|
-
seed: () => TSeed,
|
|
298
|
-
selector?: (seed: TSeed) => TReturn,
|
|
299
|
-
): TReturn => {
|
|
300
|
-
const valueRef = React.useRef<TReturn>(EMPTY as any)
|
|
301
|
-
|
|
302
|
-
// If there is no selector, track the seed
|
|
303
|
-
// If there is a selector, do not track the seed
|
|
304
|
-
const getValue = () =>
|
|
305
|
-
(!selector ? seed() : selector(untrack(() => seed()))) as TReturn
|
|
306
|
-
|
|
307
|
-
// If empty, initialize the value
|
|
308
|
-
if (valueRef.current === EMPTY) {
|
|
309
|
-
valueRef.current = sharedClone(undefined, getValue())
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Snapshot should just return the current cached value
|
|
313
|
-
const getSnapshot = React.useCallback(() => valueRef.current, [])
|
|
314
|
-
|
|
315
|
-
const getStore = React.useCallback((cb: () => void) => {
|
|
316
|
-
// A root is necessary to track effects
|
|
317
|
-
return createRoot(() => {
|
|
318
|
-
createEffect(() => {
|
|
319
|
-
// Read and update the value
|
|
320
|
-
// getValue will handle which values are accessed and
|
|
321
|
-
// thus tracked.
|
|
322
|
-
// sharedClone will both recursively track the end result
|
|
323
|
-
// and ensure that the previous value is structurally shared
|
|
324
|
-
// into the new version.
|
|
325
|
-
valueRef.current = unwrap(
|
|
326
|
-
// Unwrap the value to get rid of any proxy structures
|
|
327
|
-
sharedClone(valueRef.current, getValue()),
|
|
328
|
-
)
|
|
329
|
-
cb()
|
|
330
|
-
})
|
|
331
|
-
})
|
|
332
|
-
}, [])
|
|
333
|
-
|
|
334
|
-
return useSyncExternalStore(getStore, getSnapshot, getSnapshot)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const [store, setStore] = createStore({ foo: 'foo', bar: { baz: 'baz' } })
|
|
338
|
-
|
|
339
|
-
createRoot(() => {
|
|
340
|
-
let prev: any
|
|
341
|
-
|
|
342
|
-
createEffect(() => {
|
|
343
|
-
console.log('effect')
|
|
344
|
-
const next = sharedClone(prev, store)
|
|
345
|
-
console.log(next)
|
|
346
|
-
prev = untrack(() => next)
|
|
347
|
-
})
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
setStore((s) => {
|
|
351
|
-
s.foo = '1'
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
setStore((s) => {
|
|
355
|
-
s.bar.baz = '2'
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
export function createReactRouter<
|
|
293
|
+
export class ReactRouter<
|
|
359
294
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
360
295
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
361
296
|
TRouterContext = unknown,
|
|
362
|
-
>
|
|
363
|
-
opts: RouterOptions<TRouteConfig, TRouterContext
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return component as any
|
|
373
|
-
},
|
|
374
|
-
})
|
|
297
|
+
> extends Router<TRouteConfig, TAllRouteInfo, TRouterContext> {
|
|
298
|
+
constructor(opts: RouterOptions<TRouteConfig, TRouterContext>) {
|
|
299
|
+
super({
|
|
300
|
+
...opts,
|
|
301
|
+
loadComponent: async (component) => {
|
|
302
|
+
if (component.preload) {
|
|
303
|
+
await component.preload()
|
|
304
|
+
}
|
|
375
305
|
|
|
376
|
-
|
|
306
|
+
return component as any
|
|
307
|
+
},
|
|
308
|
+
})
|
|
309
|
+
}
|
|
377
310
|
}
|
|
378
311
|
|
|
379
312
|
export type RouterProps<
|
|
@@ -394,15 +327,14 @@ export function RouterProvider<
|
|
|
394
327
|
}: RouterProps<TRouteConfig, TAllRouteInfo, TRouterContext>) {
|
|
395
328
|
router.update(rest)
|
|
396
329
|
|
|
397
|
-
const [, , currentMatches] =
|
|
398
|
-
|
|
330
|
+
const [, , currentMatches] = useStore(
|
|
331
|
+
router.store,
|
|
399
332
|
(s) => [s.status, s.pendingMatches, s.currentMatches],
|
|
333
|
+
true,
|
|
400
334
|
)
|
|
401
335
|
|
|
402
336
|
React.useEffect(router.mount, [router])
|
|
403
337
|
|
|
404
|
-
console.log('current', currentMatches)
|
|
405
|
-
|
|
406
338
|
return (
|
|
407
339
|
<>
|
|
408
340
|
<routerContext.Provider value={{ router: router as any }}>
|
|
@@ -422,9 +354,10 @@ export function useRouter(): RegisteredRouter {
|
|
|
422
354
|
|
|
423
355
|
export function useRouterStore<T = RouterStore>(
|
|
424
356
|
selector?: (state: Router['store']) => T,
|
|
357
|
+
shallow?: boolean,
|
|
425
358
|
): T {
|
|
426
359
|
const router = useRouter()
|
|
427
|
-
return
|
|
360
|
+
return useStore(router.store, selector as any, shallow)
|
|
428
361
|
}
|
|
429
362
|
|
|
430
363
|
export function useMatches(): RouteMatch[] {
|
|
@@ -438,16 +371,16 @@ export function useMatch<
|
|
|
438
371
|
RegisteredAllRouteInfo,
|
|
439
372
|
RegisteredAllRouteInfo['routeInfoById'][TFrom]
|
|
440
373
|
>,
|
|
441
|
-
// TSelected = TRouteMatch,
|
|
442
374
|
>(opts?: {
|
|
443
375
|
from: TFrom
|
|
444
376
|
strict?: TStrict
|
|
445
|
-
|
|
377
|
+
track?: (match: TRouteMatch) => any
|
|
378
|
+
shallow?: boolean
|
|
446
379
|
}): TStrict extends true ? TRouteMatch : TRouteMatch | undefined {
|
|
447
380
|
const router = useRouter()
|
|
448
381
|
const nearestMatch = useMatches()[0]!
|
|
449
382
|
const match = opts?.from
|
|
450
|
-
? router.store.currentMatches.find((d) => d.
|
|
383
|
+
? router.store.state.currentMatches.find((d) => d.route.id === opts?.from)
|
|
451
384
|
: nearestMatch
|
|
452
385
|
|
|
453
386
|
invariant(
|
|
@@ -459,20 +392,24 @@ export function useMatch<
|
|
|
459
392
|
|
|
460
393
|
if (opts?.strict ?? true) {
|
|
461
394
|
invariant(
|
|
462
|
-
nearestMatch.
|
|
395
|
+
nearestMatch.route.id == match?.route.id,
|
|
463
396
|
`useMatch("${
|
|
464
|
-
match?.
|
|
397
|
+
match?.route.id as string
|
|
465
398
|
}") is being called in a component that is meant to render the '${
|
|
466
|
-
nearestMatch.
|
|
399
|
+
nearestMatch.route.id
|
|
467
400
|
}' route. Did you mean to 'useMatch("${
|
|
468
|
-
match?.
|
|
401
|
+
match?.route.id as string
|
|
469
402
|
}", { strict: false })' or 'useRoute("${
|
|
470
|
-
match?.
|
|
403
|
+
match?.route.id as string
|
|
471
404
|
}")' instead?`,
|
|
472
405
|
)
|
|
473
406
|
}
|
|
474
407
|
|
|
475
|
-
|
|
408
|
+
useStore(
|
|
409
|
+
match!.store,
|
|
410
|
+
(d) => opts?.track?.(match as any) ?? match,
|
|
411
|
+
opts?.shallow,
|
|
412
|
+
)
|
|
476
413
|
|
|
477
414
|
return match as any
|
|
478
415
|
}
|
|
@@ -499,14 +436,26 @@ export function useLoaderData<
|
|
|
499
436
|
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
500
437
|
TStrict extends boolean = true,
|
|
501
438
|
TLoaderData = RegisteredAllRouteInfo['routeInfoById'][TFrom]['loaderData'],
|
|
502
|
-
TSelected = TLoaderData,
|
|
503
439
|
>(opts?: {
|
|
504
440
|
from: TFrom
|
|
505
441
|
strict?: TStrict
|
|
506
|
-
|
|
507
|
-
}): TStrict extends true ?
|
|
508
|
-
const match = useMatch(opts)
|
|
509
|
-
|
|
442
|
+
track?: (loaderData: TLoaderData) => any
|
|
443
|
+
}): TStrict extends true ? TLoaderData : TLoaderData | undefined {
|
|
444
|
+
const match = useMatch(opts)
|
|
445
|
+
|
|
446
|
+
invariant(
|
|
447
|
+
match,
|
|
448
|
+
`Could not find ${
|
|
449
|
+
opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'
|
|
450
|
+
}`,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
useStore(
|
|
454
|
+
(match as any).store,
|
|
455
|
+
(d: any) => opts?.track?.(d.loaderData) ?? d.loaderData,
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
return (match as unknown as RouteMatch).store.state.loaderData as any
|
|
510
459
|
}
|
|
511
460
|
|
|
512
461
|
export function useSearch<
|
|
@@ -517,10 +466,15 @@ export function useSearch<
|
|
|
517
466
|
>(opts?: {
|
|
518
467
|
from: TFrom
|
|
519
468
|
strict?: TStrict
|
|
520
|
-
|
|
469
|
+
track?: (search: TSearch) => TSelected
|
|
521
470
|
}): TStrict extends true ? TSelected : TSelected | undefined {
|
|
522
471
|
const match = useMatch(opts)
|
|
523
|
-
|
|
472
|
+
useStore(
|
|
473
|
+
(match as any).store,
|
|
474
|
+
(d: any) => opts?.track?.(d.search) ?? d.search,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
return (match as unknown as RouteMatch).store.state.search as any
|
|
524
478
|
}
|
|
525
479
|
|
|
526
480
|
export function useParams<
|
|
@@ -532,13 +486,15 @@ export function useParams<
|
|
|
532
486
|
TSelected = TDefaultSelected,
|
|
533
487
|
>(opts?: {
|
|
534
488
|
from: TFrom
|
|
535
|
-
|
|
489
|
+
track?: (search: TDefaultSelected) => TSelected
|
|
536
490
|
}): TSelected {
|
|
537
491
|
const router = useRouter()
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
opts?.
|
|
541
|
-
)
|
|
492
|
+
useStore(router.store, (d) => {
|
|
493
|
+
const params = last(d.currentMatches)?.params as any
|
|
494
|
+
return opts?.track?.(params) ?? params
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
return last(router.store.state.currentMatches)?.params as any
|
|
542
498
|
}
|
|
543
499
|
|
|
544
500
|
export function useNavigate<
|
|
@@ -555,18 +511,6 @@ export function useNavigate<
|
|
|
555
511
|
}
|
|
556
512
|
}
|
|
557
513
|
|
|
558
|
-
export function useAction<
|
|
559
|
-
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
560
|
-
TFromRoute extends RegisteredAllRouteInfo['routeInfoById'][TFrom] = RegisteredAllRouteInfo['routeInfoById'][TFrom],
|
|
561
|
-
>(opts: {
|
|
562
|
-
from: TFrom
|
|
563
|
-
}): Action<TFromRoute['actionPayload'], TFromRoute['actionResponse']> {
|
|
564
|
-
const route = useRoute(opts.from)
|
|
565
|
-
const action = route.action
|
|
566
|
-
__useStoreValue(() => action)
|
|
567
|
-
return action as any
|
|
568
|
-
}
|
|
569
|
-
|
|
570
514
|
export function useMatchRoute() {
|
|
571
515
|
const router = useRouter()
|
|
572
516
|
|
|
@@ -596,38 +540,51 @@ export function MatchRoute<
|
|
|
596
540
|
return null
|
|
597
541
|
}
|
|
598
542
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
)
|
|
543
|
+
if (typeof props.children === 'function') {
|
|
544
|
+
return (props.children as any)(params)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return params ? props.children : null
|
|
605
548
|
}
|
|
606
549
|
|
|
607
550
|
export function Outlet() {
|
|
608
|
-
const router = useRouter()
|
|
609
551
|
const matches = useMatches().slice(1)
|
|
610
552
|
const match = matches[0]
|
|
611
553
|
|
|
612
|
-
|
|
554
|
+
if (!match) {
|
|
555
|
+
return null
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return <SubOutlet matches={matches} match={match} />
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function SubOutlet({
|
|
562
|
+
matches,
|
|
563
|
+
match,
|
|
564
|
+
}: {
|
|
565
|
+
matches: RouteMatch[]
|
|
566
|
+
match: RouteMatch
|
|
567
|
+
}) {
|
|
568
|
+
const router = useRouter()
|
|
569
|
+
useStore(match!.store)
|
|
613
570
|
|
|
614
|
-
|
|
571
|
+
const defaultPending = React.useCallback(() => null, [])
|
|
615
572
|
|
|
616
573
|
const Inner = React.useCallback((props: { match: RouteMatch }): any => {
|
|
617
|
-
if (props.match.store.status === 'error') {
|
|
618
|
-
throw props.match.store.error
|
|
574
|
+
if (props.match.store.state.status === 'error') {
|
|
575
|
+
throw props.match.store.state.error
|
|
619
576
|
}
|
|
620
577
|
|
|
621
|
-
if (props.match.store.status === 'success') {
|
|
578
|
+
if (props.match.store.state.status === 'success') {
|
|
622
579
|
return React.createElement(
|
|
623
|
-
(props.match.
|
|
580
|
+
(props.match.component as any) ??
|
|
624
581
|
router.options.defaultComponent ??
|
|
625
582
|
Outlet,
|
|
626
583
|
)
|
|
627
584
|
}
|
|
628
585
|
|
|
629
|
-
if (props.match.store.status === 'loading') {
|
|
630
|
-
throw props.match.
|
|
586
|
+
if (props.match.store.state.status === 'loading') {
|
|
587
|
+
throw props.match.__loadPromise
|
|
631
588
|
}
|
|
632
589
|
|
|
633
590
|
invariant(
|
|
@@ -636,22 +593,18 @@ export function Outlet() {
|
|
|
636
593
|
)
|
|
637
594
|
}, [])
|
|
638
595
|
|
|
639
|
-
|
|
640
|
-
return null
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const PendingComponent = (match.__.pendingComponent ??
|
|
596
|
+
const PendingComponent = (match.pendingComponent ??
|
|
644
597
|
router.options.defaultPendingComponent ??
|
|
645
598
|
defaultPending) as any
|
|
646
599
|
|
|
647
600
|
const errorComponent =
|
|
648
|
-
match.
|
|
601
|
+
match.errorComponent ?? router.options.defaultErrorComponent
|
|
649
602
|
|
|
650
603
|
return (
|
|
651
604
|
<matchesContext.Provider value={matches}>
|
|
652
605
|
<React.Suspense fallback={<PendingComponent />}>
|
|
653
606
|
<CatchBoundary
|
|
654
|
-
key={match.
|
|
607
|
+
key={match.route.id}
|
|
655
608
|
errorComponent={errorComponent}
|
|
656
609
|
match={match as any}
|
|
657
610
|
>
|
|
@@ -686,7 +639,7 @@ class CatchBoundary extends React.Component<{
|
|
|
686
639
|
}
|
|
687
640
|
|
|
688
641
|
componentDidCatch(error: any, info: any) {
|
|
689
|
-
console.error(`Error in route match: ${this.props.match.
|
|
642
|
+
console.error(`Error in route match: ${this.props.match.id}`)
|
|
690
643
|
console.error(error)
|
|
691
644
|
|
|
692
645
|
this.setState({
|
|
@@ -721,21 +674,23 @@ function CatchBoundaryInner(props: {
|
|
|
721
674
|
const router = useRouter()
|
|
722
675
|
const errorComponent = props.errorComponent ?? DefaultErrorBoundary
|
|
723
676
|
|
|
724
|
-
React.useEffect(() => {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
)
|
|
735
|
-
}
|
|
677
|
+
// React.useEffect(() => {
|
|
678
|
+
// if (activeErrorState) {
|
|
679
|
+
// let prevKey = router.store.currentLocation.key
|
|
680
|
+
// return createRoot((dispose) => {
|
|
681
|
+
// createEffect(() => {
|
|
682
|
+
// if (router.store.currentLocation.key !== prevKey) {
|
|
683
|
+
// prevKey = router.store.currentLocation.key
|
|
684
|
+
// setActiveErrorState({} as any)
|
|
685
|
+
// }
|
|
686
|
+
// })
|
|
736
687
|
|
|
737
|
-
|
|
738
|
-
}
|
|
688
|
+
// return dispose
|
|
689
|
+
// })
|
|
690
|
+
// }
|
|
691
|
+
|
|
692
|
+
// return
|
|
693
|
+
// }, [activeErrorState])
|
|
739
694
|
|
|
740
695
|
React.useEffect(() => {
|
|
741
696
|
if (props.errorState.error) {
|
|
@@ -777,43 +732,61 @@ export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
|
777
732
|
)
|
|
778
733
|
}
|
|
779
734
|
|
|
780
|
-
export function
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
735
|
+
export function useAction<
|
|
736
|
+
TKey extends string = string,
|
|
737
|
+
TPayload = unknown,
|
|
738
|
+
TResponse = unknown,
|
|
739
|
+
TError = Error,
|
|
740
|
+
>(
|
|
741
|
+
action: Action<TKey, TPayload, TResponse, TError>,
|
|
742
|
+
opts?: {
|
|
743
|
+
track?: (actionStore: ActionStore<TPayload, TResponse, TError>) => any
|
|
744
|
+
},
|
|
745
|
+
): Action & {
|
|
746
|
+
latestSubmission: ActionSubmission<TPayload, TResponse, TError>
|
|
747
|
+
pendingSubmissions: ActionSubmission<TPayload, TResponse, TError>[]
|
|
748
|
+
} {
|
|
749
|
+
useStore(action.store, (d) => opts?.track?.(d) ?? d, true)
|
|
750
|
+
|
|
751
|
+
const [ref] = React.useState({})
|
|
752
|
+
|
|
753
|
+
Object.assign(ref, {
|
|
754
|
+
...action,
|
|
755
|
+
latestSubmission:
|
|
756
|
+
action.store.state.submissions[action.store.state.submissions.length - 1],
|
|
757
|
+
pendingSubmissions: React.useMemo(
|
|
758
|
+
() =>
|
|
759
|
+
action.store.state.submissions.filter((d) => d.status === 'pending'),
|
|
760
|
+
[action.store.state.submissions],
|
|
761
|
+
),
|
|
762
|
+
})
|
|
794
763
|
|
|
795
|
-
|
|
796
|
-
}, [when, message])
|
|
764
|
+
return ref as any
|
|
797
765
|
}
|
|
798
766
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
767
|
+
// TODO: While we migrate away from the history package, these need to be disabled
|
|
768
|
+
// export function usePrompt(message: string, when: boolean | any): void {
|
|
769
|
+
// const router = useRouter()
|
|
770
|
+
|
|
771
|
+
// React.useEffect(() => {
|
|
772
|
+
// if (!when) return
|
|
773
|
+
|
|
774
|
+
// let unblock = router.getHistory().block((transition) => {
|
|
775
|
+
// if (window.confirm(message)) {
|
|
776
|
+
// unblock()
|
|
777
|
+
// transition.retry()
|
|
778
|
+
// } else {
|
|
779
|
+
// router.setStore((s) => {
|
|
780
|
+
// s.currentLocation.pathname = window.location.pathname
|
|
781
|
+
// })
|
|
782
|
+
// }
|
|
783
|
+
// })
|
|
803
784
|
|
|
804
|
-
//
|
|
805
|
-
//
|
|
785
|
+
// return unblock
|
|
786
|
+
// }, [when, message])
|
|
787
|
+
// }
|
|
806
788
|
|
|
807
|
-
//
|
|
808
|
-
//
|
|
809
|
-
//
|
|
810
|
-
// return undefined
|
|
811
|
-
// }
|
|
812
|
-
// if (typeof value === 'object' && value !== null) {
|
|
813
|
-
// if (seen.has(value)) return
|
|
814
|
-
// seen.add(value)
|
|
815
|
-
// }
|
|
816
|
-
// return value
|
|
817
|
-
// }) || ''
|
|
818
|
-
// )
|
|
789
|
+
// export function Prompt({ message, when, children }: PromptProps) {
|
|
790
|
+
// usePrompt(message, when ?? true)
|
|
791
|
+
// return (children ?? null) as ReactNode
|
|
819
792
|
// }
|
package/src/useStore.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Store } from '@tanstack/router-core'
|
|
2
|
+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
|
|
3
|
+
|
|
4
|
+
export function useStore<TState, TSelected = TState>(
|
|
5
|
+
store: Store<TState>,
|
|
6
|
+
selector: (state: TState) => TSelected = (d) => d as any,
|
|
7
|
+
compareShallow?: boolean,
|
|
8
|
+
) {
|
|
9
|
+
const slice = useSyncExternalStoreWithSelector(
|
|
10
|
+
store.subscribe,
|
|
11
|
+
() => store.state,
|
|
12
|
+
() => store.state,
|
|
13
|
+
selector,
|
|
14
|
+
compareShallow ? shallow : undefined,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
return slice
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function shallow<T>(objA: T, objB: T) {
|
|
21
|
+
if (Object.is(objA, objB)) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
typeof objA !== 'object' ||
|
|
27
|
+
objA === null ||
|
|
28
|
+
typeof objB !== 'object' ||
|
|
29
|
+
objB === null
|
|
30
|
+
) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// if (objA instanceof Map && objB instanceof Map) {
|
|
35
|
+
// if (objA.size !== objB.size) return false
|
|
36
|
+
|
|
37
|
+
// for (const [key, value] of objA) {
|
|
38
|
+
// if (!Object.is(value, objB.get(key))) {
|
|
39
|
+
// return false
|
|
40
|
+
// }
|
|
41
|
+
// }
|
|
42
|
+
// return true
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// if (objA instanceof Set && objB instanceof Set) {
|
|
46
|
+
// if (objA.size !== objB.size) return false
|
|
47
|
+
|
|
48
|
+
// for (const value of objA) {
|
|
49
|
+
// if (!objB.has(value)) {
|
|
50
|
+
// return false
|
|
51
|
+
// }
|
|
52
|
+
// }
|
|
53
|
+
// return true
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
const keysA = Object.keys(objA)
|
|
57
|
+
if (keysA.length !== Object.keys(objB).length) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
62
|
+
if (
|
|
63
|
+
!Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) ||
|
|
64
|
+
!Object.is(objA[keysA[i] as keyof T], objB[keysA[i] as keyof T])
|
|
65
|
+
) {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
* @flow
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict'
|
|
11
|
-
|
|
12
|
-
// Intentionally not using named imports because Rollup uses dynamic
|
|
13
|
-
// dispatch for CommonJS interop named imports.
|
|
14
|
-
import * as React from 'react'
|
|
15
|
-
|
|
16
|
-
export const useSyncExternalStore = React.useSyncExternalStore
|