@@ -20,7 +20,6 @@ import type {
2020import type { ReactNodeList } from 'shared/ReactTypes' ;
2121
2222import { REACT_MEMO_TYPE , REACT_FORWARD_REF_TYPE } from 'shared/ReactSymbols' ;
23- import warningWithoutStack from 'shared/warningWithoutStack' ;
2423
2524type Signature = { |
2625 ownKey : string ,
@@ -29,6 +28,13 @@ type Signature = {|
2928 getCustomHooks : ( ) => Array < Function > ,
3029| } ;
3130
31+ type RendererHelpers = { |
32+ findHostInstancesForRefresh : FindHostInstancesForRefresh ,
33+ scheduleRefresh : ScheduleRefresh ,
34+ scheduleRoot : ScheduleRoot ,
35+ setRefreshHandler : SetRefreshHandler ,
36+ | } ;
37+
3238if ( ! __DEV__ ) {
3339 throw new Error (
3440 'React Refresh runtime should not be included in the production bundle.' ,
@@ -56,10 +62,9 @@ WeakMap<any, Family> | Map<any, Family> = new PossiblyWeakMap();
5662let pendingUpdates : Array < [ Family , any ] > = [ ] ;
5763
5864// This is injected by the renderer via DevTools global hook.
59- let setRefreshHandler : null | SetRefreshHandler = null ;
60- let scheduleRefresh : null | ScheduleRefresh = null ;
61- let scheduleRoot : null | ScheduleRoot = null ;
62- let findHostInstancesForRefresh : null | FindHostInstancesForRefresh = null ;
65+ let helpersByRendererID : Map < number , RendererHelpers > = new Map ( ) ;
66+
67+ let helpersByRoot : Map < FiberRoot , RendererHelpers > = new Map ( ) ;
6368
6469// We keep track of mounted roots so we can schedule updates.
6570let mountedRoots : Set < FiberRoot > = new Set ( ) ;
@@ -182,49 +187,23 @@ export function performReactRefresh(): RefreshUpdate | null {
182187 staleFamilies, // Families that will be remounted
183188 } ;
184189
185- if ( typeof setRefreshHandler !== 'function' ) {
186- warningWithoutStack (
187- false ,
188- 'Could not find the setRefreshHandler() implementation. ' +
189- 'This likely means that injectIntoGlobalHook() was either ' +
190- 'called before the global DevTools hook was set up, or after the ' +
191- 'renderer has already initialized. Please file an issue with a reproducing case.' ,
192- ) ;
193- return null ;
194- }
195-
196- if ( typeof scheduleRefresh !== 'function' ) {
197- warningWithoutStack (
198- false ,
199- 'Could not find the scheduleRefresh() implementation. ' +
200- 'This likely means that injectIntoGlobalHook() was either ' +
201- 'called before the global DevTools hook was set up, or after the ' +
202- 'renderer has already initialized. Please file an issue with a reproducing case.' ,
203- ) ;
204- return null ;
205- }
206- if ( typeof scheduleRoot !== 'function' ) {
207- warningWithoutStack (
208- false ,
209- 'Could not find the scheduleRoot() implementation. ' +
210- 'This likely means that injectIntoGlobalHook() was either ' +
211- 'called before the global DevTools hook was set up, or after the ' +
212- 'renderer has already initialized. Please file an issue with a reproducing case.' ,
213- ) ;
214- return null ;
215- }
216- const scheduleRefreshForRoot = scheduleRefresh ;
217- const scheduleRenderForRoot = scheduleRoot ;
218-
219- // Even if there are no roots, set the handler on first update.
220- // This ensures that if *new* roots are mounted, they'll use the resolve handler.
221- setRefreshHandler ( resolveFamily ) ;
190+ helpersByRendererID . forEach ( helpers => {
191+ // Even if there are no roots, set the handler on first update.
192+ // This ensures that if *new* roots are mounted, they'll use the resolve handler.
193+ helpers . setRefreshHandler ( resolveFamily ) ;
194+ } ) ;
222195
223196 let didError = false ;
224197 let firstError = null ;
225198 failedRoots . forEach ( ( element , root ) => {
199+ const helpers = helpersByRoot . get ( root ) ;
200+ if ( helpers === undefined ) {
201+ throw new Error (
202+ 'Could not find helpers for a root. This is a bug in React Refresh.' ,
203+ ) ;
204+ }
226205 try {
227- scheduleRenderForRoot ( root , element ) ;
206+ helpers . scheduleRoot ( root , element ) ;
228207 } catch ( err ) {
229208 if ( ! didError ) {
230209 didError = true ;
@@ -234,8 +213,14 @@ export function performReactRefresh(): RefreshUpdate | null {
234213 }
235214 } ) ;
236215 mountedRoots . forEach ( root => {
216+ const helpers = helpersByRoot . get ( root ) ;
217+ if ( helpers === undefined ) {
218+ throw new Error (
219+ 'Could not find helpers for a root. This is a bug in React Refresh.' ,
220+ ) ;
221+ }
237222 try {
238- scheduleRefreshForRoot ( root , update ) ;
223+ helpers . scheduleRefresh ( root , update ) ;
239224 } catch ( err ) {
240225 if ( ! didError ) {
241226 didError = true ;
@@ -359,20 +344,18 @@ export function findAffectedHostInstances(
359344 families : Array < Family > ,
360345) : Set < Instance > {
361346 if ( __DEV__ ) {
362- if ( typeof findHostInstancesForRefresh !== 'function' ) {
363- warningWithoutStack (
364- false ,
365- 'Could not find the findHostInstancesForRefresh() implementation. ' +
366- 'This likely means that injectIntoGlobalHook() was either ' +
367- 'called before the global DevTools hook was set up, or after the ' +
368- 'renderer has already initialized. Please file an issue with a reproducing case.' ,
369- ) ;
370- return new Set ( ) ;
371- }
372- const findInstances = findHostInstancesForRefresh ;
373347 let affectedInstances = new Set ( ) ;
374348 mountedRoots . forEach ( root => {
375- const instancesForRoot = findInstances ( root , families ) ;
349+ const helpers = helpersByRoot . get ( root ) ;
350+ if ( helpers === undefined ) {
351+ throw new Error (
352+ 'Could not find helpers for a root. This is a bug in React Refresh.' ,
353+ ) ;
354+ }
355+ const instancesForRoot = helpers . findHostInstancesForRefresh (
356+ root ,
357+ families ,
358+ ) ;
376359 instancesForRoot . forEach ( inst => {
377360 affectedInstances . add ( inst ) ;
378361 } ) ;
@@ -397,11 +380,14 @@ export function injectIntoGlobalHook(globalObject: any): void {
397380 // However, if there is no DevTools extension, we'll need to set up the global hook ourselves.
398381 // Note that in this case it's important that renderer code runs *after* this method call.
399382 // Otherwise, the renderer will think that there is no global hook, and won't do the injection.
383+ let nextID = 0 ;
400384 globalObject . __REACT_DEVTOOLS_GLOBAL_HOOK__ = hook = {
401385 supportsFiber : true ,
402- inject ( ) { } ,
386+ inject ( injected ) {
387+ return nextID ++ ;
388+ } ,
403389 onCommitFiberRoot (
404- id : mixed ,
390+ id : number ,
405391 root : FiberRoot ,
406392 maybePriorityLevel : mixed ,
407393 didError : boolean ,
@@ -413,23 +399,31 @@ export function injectIntoGlobalHook(globalObject: any): void {
413399 // Here, we just want to get a reference to scheduleRefresh.
414400 const oldInject = hook . inject ;
415401 hook . inject = function ( injected ) {
416- findHostInstancesForRefresh = ( ( injected : any )
417- . findHostInstancesForRefresh : FindHostInstancesForRefresh ) ;
418- scheduleRefresh = ( ( injected : any ) . scheduleRefresh : ScheduleRefresh ) ;
419- scheduleRoot = ( ( injected : any ) . scheduleRoot : ScheduleRoot ) ;
420- setRefreshHandler = ( ( injected : any )
421- . setRefreshHandler : SetRefreshHandler ) ;
422- return oldInject . apply ( this , arguments ) ;
402+ const id = oldInject . apply ( this , arguments ) ;
403+ if (
404+ typeof injected . scheduleRefresh === 'function' &&
405+ typeof injected . setRefreshHandler === 'function'
406+ ) {
407+ // This version supports React Refresh.
408+ helpersByRendererID . set ( id , ( ( injected : any ) : RendererHelpers ) ) ;
409+ }
410+ return id ;
423411 } ;
424412
425413 // We also want to track currently mounted roots.
426414 const oldOnCommitFiberRoot = hook . onCommitFiberRoot ;
427415 hook . onCommitFiberRoot = function (
428- id : mixed ,
416+ id : number ,
429417 root : FiberRoot ,
430418 maybePriorityLevel : mixed ,
431419 didError : boolean ,
432420 ) {
421+ const helpers = helpersByRendererID . get ( id ) ;
422+ if ( helpers === undefined ) {
423+ return ;
424+ }
425+ helpersByRoot . set ( root , helpers ) ;
426+
433427 const current = root . current ;
434428 const alternate = current . alternate ;
435429
@@ -459,6 +453,8 @@ export function injectIntoGlobalHook(globalObject: any): void {
459453 // We'll remount it on future edits.
460454 // Remember what was rendered so we can restore it.
461455 failedRoots . set ( root , alternate . memoizedState . element ) ;
456+ } else {
457+ helpersByRoot . delete ( root ) ;
462458 }
463459 } else if ( ! wasMounted && ! isMounted ) {
464460 if ( didError && ! failedRoots . has ( root ) ) {
0 commit comments