@@ -58,19 +58,32 @@ const checkStatus = async (
58
58
59
59
type Elements = Record < string , unknown > ;
60
60
61
+ // HACK I'm not super happy with this hack
62
+ const erroredElementsPromiseMap = new WeakMap <
63
+ Promise < Elements > ,
64
+ Promise < Elements >
65
+ > ( ) ;
66
+
61
67
const getCached = < T > ( c : ( ) => T , m : WeakMap < object , T > , k : object ) : T =>
62
68
( m . has ( k ) ? m : m . set ( k , c ( ) ) ) . get ( k ) as T ;
63
69
const cache1 = new WeakMap ( ) ;
64
70
const mergeElementsPromise = (
65
71
a : Promise < Elements > ,
66
72
b : Promise < Elements > ,
67
73
) : Promise < Elements > => {
68
- const getResult = ( ) =>
69
- Promise . all ( [ a , b ] ) . then ( ( [ a , b ] ) => {
70
- const nextElements = { ...a , ...b } ;
71
- delete nextElements . _value ;
72
- return nextElements ;
73
- } ) ;
74
+ const getResult = ( ) => {
75
+ const p = Promise . all ( [ erroredElementsPromiseMap . get ( a ) || a , b ] )
76
+ . then ( ( [ a , b ] ) => {
77
+ const nextElements = { ...a , ...b } ;
78
+ delete nextElements . _value ;
79
+ return nextElements ;
80
+ } )
81
+ . catch ( ( err ) => {
82
+ erroredElementsPromiseMap . set ( p , a ) ;
83
+ throw err ;
84
+ } ) ;
85
+ return p ;
86
+ } ;
74
87
const cache2 = getCached ( ( ) => new WeakMap ( ) , cache1 , a ) ;
75
88
return getCached ( getResult , cache2 , b ) ;
76
89
} ;
@@ -248,6 +261,27 @@ export const useRefetch = () => use(RefetchContext);
248
261
249
262
const ChildrenContext = createContext < ReactNode > ( undefined ) ;
250
263
const ChildrenContextProvider = memo ( ChildrenContext . Provider ) ;
264
+ const ErrorContext = createContext <
265
+ [ error : unknown , reset : ( ) => void ] | undefined
266
+ > ( undefined ) ;
267
+ const ErrorContextProvider = memo ( ErrorContext . Provider ) ;
268
+
269
+ export const Children = ( ) => use ( ChildrenContext ) ;
270
+
271
+ export const ThrowError_UNSTABLE = ( ) => {
272
+ const errAndReset = use ( ErrorContext ) ;
273
+ if ( errAndReset ) {
274
+ throw errAndReset [ 0 ] ;
275
+ }
276
+ return null ;
277
+ } ;
278
+
279
+ export const useResetError_UNSTABLE = ( ) => {
280
+ const errAndReset = use ( ErrorContext ) ;
281
+ if ( errAndReset ) {
282
+ return errAndReset [ 1 ] ;
283
+ }
284
+ } ;
251
285
252
286
export const useElement = ( id : string ) => {
253
287
const elementsPromise = use ( ElementsContext ) ;
@@ -264,22 +298,22 @@ export const useElement = (id: string) => {
264
298
const InnerSlot = ( {
265
299
id,
266
300
children,
267
- setFallback ,
301
+ setValidElement ,
268
302
unstable_fallback,
269
303
} : {
270
304
id : string ;
271
305
children ?: ReactNode ;
272
- setFallback ?: ( fallback : ReactNode ) => void ;
306
+ setValidElement ?: ( element : ReactNode ) => void ;
273
307
unstable_fallback ?: ReactNode ;
274
308
} ) => {
275
309
const element = useElement ( id ) ;
276
310
const isValidElement = element !== undefined ;
277
311
useEffect ( ( ) => {
278
- if ( isValidElement && setFallback ) {
312
+ if ( isValidElement && setValidElement ) {
279
313
// FIXME is there `isReactNode` type checker?
280
- setFallback ( element as ReactNode ) ;
314
+ setValidElement ( element as ReactNode ) ;
281
315
}
282
- } , [ isValidElement , element , setFallback ] ) ;
316
+ } , [ isValidElement , element , setValidElement ] ) ;
283
317
if ( ! isValidElement ) {
284
318
if ( unstable_fallback ) {
285
319
return unstable_fallback ;
@@ -294,31 +328,32 @@ const InnerSlot = ({
294
328
) ;
295
329
} ;
296
330
297
- const ThrowError = ( { error } : { error : unknown } ) => {
298
- throw error ;
299
- } ;
300
-
301
- class Fallback extends Component <
302
- { children : ReactNode ; fallback : ReactNode } ,
303
- { error ?: unknown }
331
+ class GeneralErrorHandler extends Component <
332
+ { children ?: ReactNode ; errorHandler : ReactNode } ,
333
+ { error : unknown | null }
304
334
> {
305
- constructor ( props : { children : ReactNode ; fallback : ReactNode } ) {
335
+ constructor ( props : { children ? : ReactNode ; errorHandler : ReactNode } ) {
306
336
super ( props ) ;
307
- this . state = { } ;
337
+ this . state = { error : null } ;
338
+ this . reset = this . reset . bind ( this ) ;
308
339
}
309
340
static getDerivedStateFromError ( error : unknown ) {
310
341
return { error } ;
311
342
}
343
+ reset ( ) {
344
+ this . setState ( { error : null } ) ;
345
+ }
312
346
render ( ) {
313
- if ( 'error' in this . state ) {
314
- if ( this . props . fallback ) {
347
+ const { error } = this . state ;
348
+ if ( error !== null ) {
349
+ if ( this . props . errorHandler ) {
315
350
return createElement (
316
- ChildrenContextProvider ,
317
- { value : createElement ( ThrowError , { error : this . state . error } ) } ,
318
- this . props . fallback ,
351
+ ErrorContextProvider ,
352
+ { value : [ error , this . reset ] } ,
353
+ this . props . errorHandler ,
319
354
) ;
320
355
}
321
- throw this . state . error ;
356
+ throw error ;
322
357
}
323
358
return this . props . children ;
324
359
}
@@ -341,27 +376,36 @@ class Fallback extends Component<
341
376
export const Slot = ( {
342
377
id,
343
378
children,
344
- unstable_fallbackToPrev ,
379
+ unstable_handleError ,
345
380
unstable_fallback,
346
381
} : {
347
382
id : string ;
348
383
children ?: ReactNode ;
349
- unstable_fallbackToPrev ?: boolean ;
384
+ unstable_handleError ?: ReactNode ;
350
385
unstable_fallback ?: ReactNode ;
351
386
} ) => {
352
- const [ fallback , setFallback ] = useState < ReactNode > ( ) ;
353
- if ( unstable_fallbackToPrev ) {
387
+ const [ errorHandler , setErrorHandler ] = useState < ReactNode > ( ) ;
388
+ const setValidElement = useCallback (
389
+ ( element : ReactNode ) =>
390
+ setErrorHandler (
391
+ createElement (
392
+ ChildrenContextProvider ,
393
+ { value : unstable_handleError } ,
394
+ element ,
395
+ ) ,
396
+ ) ,
397
+ [ unstable_handleError ] ,
398
+ ) ;
399
+ if ( unstable_handleError !== undefined ) {
354
400
return createElement (
355
- Fallback ,
356
- { fallback } as never ,
357
- createElement ( InnerSlot , { id, setFallback } , children ) ,
401
+ GeneralErrorHandler ,
402
+ { errorHandler } ,
403
+ createElement ( InnerSlot , { id, setValidElement } , children ) ,
358
404
) ;
359
405
}
360
406
return createElement ( InnerSlot , { id, unstable_fallback } , children ) ;
361
407
} ;
362
408
363
- export const Children = ( ) => use ( ChildrenContext ) ;
364
-
365
409
/**
366
410
* ServerRoot for SSR
367
411
* This is not a public API.
0 commit comments