@@ -13,9 +13,11 @@ import type {
1313 Family ,
1414 RefreshUpdate ,
1515 ScheduleRefresh ,
16+ ScheduleRoot ,
1617 FindHostInstancesForRefresh ,
1718 SetRefreshHandler ,
1819} from 'react-reconciler/src/ReactFiberHotReloading' ;
20+ import type { ReactNodeList } from 'shared/ReactTypes' ;
1921
2022import { REACT_MEMO_TYPE , REACT_FORWARD_REF_TYPE } from 'shared/ReactSymbols' ;
2123import warningWithoutStack from 'shared/warningWithoutStack' ;
@@ -57,9 +59,13 @@ let pendingUpdates: Array<[Family, any]> = [];
5759// This is injected by the renderer via DevTools global hook.
5860let setRefreshHandler: null | SetRefreshHandler = null;
5961let scheduleRefresh: null | ScheduleRefresh = null;
62+ let scheduleRoot: null | ScheduleRoot = null;
6063let findHostInstancesForRefresh: null | FindHostInstancesForRefresh = null;
6164
62- let mountedRoots = new Set();
65+ // We keep track of mounted roots so we can schedule updates.
66+ let mountedRoots: Set< FiberRoot > = new Set();
67+ // If a root captures an error, we add its element to this Map so we can retry on edit.
68+ let failedRoots: Map< FiberRoot , ReactNodeList > = new Map();
6369
6470function computeFullKey(signature: Signature): string {
6571 if ( signature . fullKey !== null ) {
@@ -196,14 +202,36 @@ export function performReactRefresh(): RefreshUpdate | null {
196202 ) ;
197203 return null ;
198204 }
205+ if ( typeof scheduleRoot !== 'function ') {
206+ warningWithoutStack (
207+ false ,
208+ 'Could not find the scheduleRoot() implementation. ' +
209+ 'This likely means that injectIntoGlobalHook() was either ' +
210+ 'called before the global DevTools hook was set up, or after the ' +
211+ 'renderer has already initialized. Please file an issue with a reproducing case.' ,
212+ ) ;
213+ return null ;
214+ }
199215 const scheduleRefreshForRoot = scheduleRefresh ;
216+ const scheduleRenderForRoot = scheduleRoot ;
200217
201218 // Even if there are no roots, set the handler on first update.
202219 // This ensures that if *new* roots are mounted, they'll use the resolve handler.
203220 setRefreshHandler ( resolveFamily ) ;
204221
205222 let didError = false ;
206223 let firstError = null ;
224+ failedRoots . forEach ( ( element , root ) => {
225+ try {
226+ scheduleRenderForRoot ( root , element ) ;
227+ } catch ( err ) {
228+ if ( ! didError ) {
229+ didError = true ;
230+ firstError = err ;
231+ }
232+ // Keep trying other roots.
233+ }
234+ } ) ;
207235 mountedRoots . forEach ( root => {
208236 try {
209237 scheduleRefreshForRoot ( root , update ) ;
@@ -245,7 +273,7 @@ export function register(type: any, id: string): void {
245273
246274 // Create family or remember to update it.
247275 // None of this bookkeeping affects reconciliation
248- // until the first prepareUpdate () call above.
276+ // until the first performReactRefresh () call above.
249277 let family = allFamiliesByID . get ( id ) ;
250278 if ( family = = = undefined ) {
251279 family = { current : type } ;
@@ -362,7 +390,12 @@ export function injectIntoGlobalHook(globalObject: any): void {
362390 globalObject. __REACT_DEVTOOLS_GLOBAL_HOOK__ = hook = {
363391 supportsFiber : true ,
364392 inject ( ) { } ,
365- onCommitFiberRoot ( id : mixed , root : FiberRoot ) { } ,
393+ onCommitFiberRoot (
394+ id : mixed ,
395+ root : FiberRoot ,
396+ maybePriorityLevel : mixed ,
397+ didError : boolean ,
398+ ) { } ,
366399 onCommitFiberUnmount ( ) { } ,
367400 } ;
368401 }
@@ -373,14 +406,20 @@ export function injectIntoGlobalHook(globalObject: any): void {
373406 findHostInstancesForRefresh = ( ( injected : any )
374407 . findHostInstancesForRefresh : FindHostInstancesForRefresh ) ;
375408 scheduleRefresh = ( ( injected : any ) . scheduleRefresh : ScheduleRefresh ) ;
409+ scheduleRoot = ( ( injected : any ) . scheduleRoot : ScheduleRoot ) ;
376410 setRefreshHandler = ( ( injected : any )
377411 . setRefreshHandler : SetRefreshHandler ) ;
378412 return oldInject . apply ( this , arguments ) ;
379413 } ;
380414
381415 // We also want to track currently mounted roots.
382416 const oldOnCommitFiberRoot = hook.onCommitFiberRoot;
383- hook.onCommitFiberRoot = function(id: mixed, root: FiberRoot) {
417+ hook.onCommitFiberRoot = function(
418+ id: mixed,
419+ root: FiberRoot,
420+ maybePriorityLevel: mixed,
421+ didError: boolean,
422+ ) {
384423 const current = root . current ;
385424 const alternate = current . alternate ;
386425
@@ -399,12 +438,18 @@ export function injectIntoGlobalHook(globalObject: any): void {
399438 if ( ! wasMounted && isMounted ) {
400439 // Mount a new root.
401440 mountedRoots . add ( root ) ;
441+ failedRoots . delete ( root ) ;
402442 } else if (wasMounted && isMounted ) {
403443 // Update an existing root.
404444 // This doesn't affect our mounted root Set.
405445 } else if (wasMounted && ! isMounted ) {
406446 // Unmount an existing root.
407447 mountedRoots . delete ( root ) ;
448+ if ( didError ) {
449+ // We'll remount it on future edits.
450+ // Remember what was rendered so we can restore it.
451+ failedRoots . set ( root , alternate . memoizedState . element ) ;
452+ }
408453 }
409454 } else {
410455 // Mount a new root.
0 commit comments