@@ -349,6 +349,20 @@ if (__DEV__) {
349
349
350
350
export { ViewTransitionContext as UNSAFE_ViewTransitionContext } ;
351
351
352
+ // TODO: (v7) Change the useFetcher data from `any` to `unknown`
353
+ type FetchersContextObject = {
354
+ fetcherData : Map < string , any > ;
355
+ register : ( key : string ) => void ;
356
+ unregister : ( key : string ) => void ;
357
+ } ;
358
+
359
+ const FetchersContext = React . createContext < FetchersContextObject | null > ( null ) ;
360
+ if ( __DEV__ ) {
361
+ FetchersContext . displayName = "Fetchers" ;
362
+ }
363
+
364
+ export { FetchersContext as UNSAFE_FetchersContext } ;
365
+
352
366
//#endregion
353
367
354
368
////////////////////////////////////////////////////////////////////////////////
@@ -427,6 +441,7 @@ export function RouterProvider({
427
441
router,
428
442
future,
429
443
} : RouterProviderProps ) : React . ReactElement {
444
+ let { fetcherContext, fetcherData } = useFetcherDataLayer ( ) ;
430
445
let [ state , setStateImpl ] = React . useState ( router . state ) ;
431
446
let [ pendingState , setPendingState ] = React . useState < RouterState > ( ) ;
432
447
let [ vtContext , setVtContext ] = React . useState < ViewTransitionContextObject > ( {
@@ -457,6 +472,12 @@ export function RouterProvider({
457
472
newState : RouterState ,
458
473
{ unstable_viewTransitionOpts : viewTransitionOpts }
459
474
) => {
475
+ newState . fetchers . forEach ( ( fetcher , key ) => {
476
+ if ( fetcher . data !== undefined ) {
477
+ fetcherData . current . set ( key , fetcher . data ) ;
478
+ }
479
+ } ) ;
480
+
460
481
if (
461
482
! viewTransitionOpts ||
462
483
router . window == null ||
@@ -484,7 +505,7 @@ export function RouterProvider({
484
505
} ) ;
485
506
}
486
507
} ,
487
- [ optInStartTransition , transition , renderDfd , router . window ]
508
+ [ router . window , transition , renderDfd , fetcherData , optInStartTransition ]
488
509
) ;
489
510
490
511
// Need to use a layout effect here so we are subscribed early enough to
@@ -587,20 +608,22 @@ export function RouterProvider({
587
608
< >
588
609
< DataRouterContext . Provider value = { dataRouterContext } >
589
610
< DataRouterStateContext . Provider value = { state } >
590
- < ViewTransitionContext . Provider value = { vtContext } >
591
- < Router
592
- basename = { basename }
593
- location = { state . location }
594
- navigationType = { state . historyAction }
595
- navigator = { navigator }
596
- >
597
- { state . initialized ? (
598
- < DataRoutes routes = { router . routes } state = { state } />
599
- ) : (
600
- fallbackElement
601
- ) }
602
- </ Router >
603
- </ ViewTransitionContext . Provider >
611
+ < FetchersContext . Provider value = { fetcherContext } >
612
+ < ViewTransitionContext . Provider value = { vtContext } >
613
+ < Router
614
+ basename = { basename }
615
+ location = { state . location }
616
+ navigationType = { state . historyAction }
617
+ navigator = { navigator }
618
+ >
619
+ { state . initialized ? (
620
+ < DataRoutes routes = { router . routes } state = { state } />
621
+ ) : (
622
+ fallbackElement
623
+ ) }
624
+ </ Router >
625
+ </ ViewTransitionContext . Provider >
626
+ </ FetchersContext . Provider >
604
627
</ DataRouterStateContext . Provider >
605
628
</ DataRouterContext . Provider >
606
629
{ null }
@@ -1198,6 +1221,8 @@ enum DataRouterStateHook {
1198
1221
UseScrollRestoration = "useScrollRestoration" ,
1199
1222
}
1200
1223
1224
+ // Internal hooks
1225
+
1201
1226
function getDataRouterConsoleError (
1202
1227
hookName : DataRouterHook | DataRouterStateHook
1203
1228
) {
@@ -1216,6 +1241,49 @@ function useDataRouterState(hookName: DataRouterStateHook) {
1216
1241
return state ;
1217
1242
}
1218
1243
1244
+ function useFetcherDataLayer ( ) {
1245
+ let fetcherRefs = React . useRef < Map < string , number > > ( new Map ( ) ) ;
1246
+ let fetcherData = React . useRef < Map < string , any > > ( new Map ( ) ) ;
1247
+
1248
+ let registerFetcher = React . useCallback (
1249
+ ( key : string ) => {
1250
+ let count = fetcherRefs . current . get ( key ) ;
1251
+ if ( count == null ) {
1252
+ fetcherRefs . current . set ( key , 1 ) ;
1253
+ } else {
1254
+ fetcherRefs . current . set ( key , count + 1 ) ;
1255
+ }
1256
+ } ,
1257
+ [ fetcherRefs ]
1258
+ ) ;
1259
+
1260
+ let unregisterFetcher = React . useCallback (
1261
+ ( key : string ) => {
1262
+ let count = fetcherRefs . current . get ( key ) ;
1263
+ if ( count == null || count <= 1 ) {
1264
+ fetcherRefs . current . delete ( key ) ;
1265
+ fetcherData . current . delete ( key ) ;
1266
+ } else {
1267
+ fetcherRefs . current . set ( key , count - 1 ) ;
1268
+ }
1269
+ } ,
1270
+ [ fetcherData , fetcherRefs ]
1271
+ ) ;
1272
+
1273
+ let fetcherContext = React . useMemo < FetchersContextObject > (
1274
+ ( ) => ( {
1275
+ fetcherData : fetcherData . current ,
1276
+ register : registerFetcher ,
1277
+ unregister : unregisterFetcher ,
1278
+ } ) ,
1279
+ [ fetcherData , registerFetcher , unregisterFetcher ]
1280
+ ) ;
1281
+
1282
+ return { fetcherContext, fetcherData } ;
1283
+ }
1284
+
1285
+ // External hooks
1286
+
1219
1287
/**
1220
1288
* Handles the click behavior for router `<Link>` components. This is useful if
1221
1289
* you need to create custom `<Link>` components with the same click behavior we
@@ -1499,20 +1567,41 @@ export function useFetcher<TData = any>({
1499
1567
key,
1500
1568
} : { key ?: string } = { } ) : FetcherWithComponents < TData > {
1501
1569
let { router } = useDataRouterContext ( DataRouterHook . UseFetcher ) ;
1570
+ let fetchersContext = React . useContext ( FetchersContext ) ;
1502
1571
let route = React . useContext ( RouteContext ) ;
1503
- invariant ( route , `useFetcher must be used inside a RouteContext` ) ;
1504
-
1505
1572
let routeId = route . matches [ route . matches . length - 1 ] ?. route . id ;
1573
+
1574
+ invariant (
1575
+ fetchersContext ,
1576
+ `useFetcher must be used inside a FetchersContext`
1577
+ ) ;
1578
+ invariant ( route , `useFetcher must be used inside a RouteContext` ) ;
1506
1579
invariant (
1507
1580
routeId != null ,
1508
1581
`useFetcher can only be used on routes that contain a unique "id"`
1509
1582
) ;
1510
1583
1584
+ // Fetcher key handling
1511
1585
let [ fetcherKey , setFetcherKey ] = React . useState < string > ( key || "" ) ;
1512
1586
if ( ! fetcherKey ) {
1513
1587
setFetcherKey ( getUniqueFetcherId ( ) ) ;
1514
1588
}
1515
1589
1590
+ // Registration/cleanup
1591
+ let { fetcherData, register, unregister } = fetchersContext ;
1592
+ React . useEffect ( ( ) => {
1593
+ register ( fetcherKey ) ;
1594
+ return ( ) => {
1595
+ unregister ( fetcherKey ) ;
1596
+ if ( ! router ) {
1597
+ console . warn ( `No router available to clean up from useFetcher()` ) ;
1598
+ return ;
1599
+ }
1600
+ router . deleteFetcher ( fetcherKey ) ;
1601
+ } ;
1602
+ } , [ router , fetcherKey , register , unregister ] ) ;
1603
+
1604
+ // Fetcher additions
1516
1605
let load = React . useCallback (
1517
1606
( href : string ) => {
1518
1607
invariant ( router , "No router available for fetcher.load()" ) ;
@@ -1521,8 +1610,6 @@ export function useFetcher<TData = any>({
1521
1610
} ,
1522
1611
[ fetcherKey , routeId , router ]
1523
1612
) ;
1524
-
1525
- // Fetcher additions (submit)
1526
1613
let submitImpl = useSubmit ( ) ;
1527
1614
let submit = React . useCallback < FetcherSubmitFunction > (
1528
1615
( target , opts ) => {
@@ -1548,31 +1635,20 @@ export function useFetcher<TData = any>({
1548
1635
return FetcherForm ;
1549
1636
} , [ fetcherKey ] ) ;
1550
1637
1638
+ // Exposed FetcherWithComponents
1551
1639
let fetcher = router . getFetcher < TData > ( fetcherKey ) ;
1552
-
1640
+ let data = fetcherData . get ( fetcherKey ) ;
1553
1641
let fetcherWithComponents = React . useMemo (
1554
1642
( ) => ( {
1555
1643
Form : FetcherForm ,
1556
1644
submit,
1557
1645
load,
1558
1646
...fetcher ,
1647
+ data,
1559
1648
} ) ,
1560
- [ fetcher , FetcherForm , submit , load ]
1649
+ [ FetcherForm , submit , load , fetcher , data ]
1561
1650
) ;
1562
1651
1563
- React . useEffect ( ( ) => {
1564
- // Is this busted when the React team gets real weird and calls effects
1565
- // twice on mount? We really just need to garbage collect here when this
1566
- // fetcher is no longer around.
1567
- return ( ) => {
1568
- if ( ! router ) {
1569
- console . warn ( `No router available to clean up from useFetcher()` ) ;
1570
- return ;
1571
- }
1572
- router . deleteFetcher ( fetcherKey ) ;
1573
- } ;
1574
- } , [ router , fetcherKey ] ) ;
1575
-
1576
1652
return fetcherWithComponents ;
1577
1653
}
1578
1654
0 commit comments