@@ -72,9 +72,23 @@ export const installRDTHook = (
72
72
} ;
73
73
try {
74
74
objectDefineProperty ( globalThis , '__REACT_DEVTOOLS_GLOBAL_HOOK__' , {
75
- value : rdtHook ,
75
+ get ( ) {
76
+ return rdtHook ;
77
+ } ,
78
+ set ( newHook ) {
79
+ if ( newHook && typeof newHook === 'object' ) {
80
+ const ourRenderers = rdtHook . renderers ;
81
+ globalThis . __REACT_DEVTOOLS_GLOBAL_HOOK__ = newHook ;
82
+ if ( ourRenderers . size > 0 ) {
83
+ ourRenderers . forEach ( ( renderer , id ) => {
84
+ newHook . renderers . set ( id , renderer ) ;
85
+ } ) ;
86
+ patchRDTHook ( onActive ) ;
87
+ }
88
+ }
89
+ } ,
76
90
configurable : true ,
77
- writable : true ,
91
+ enumerable : true ,
78
92
} ) ;
79
93
// [!] this is a hack for chrome extensions - if we install before React DevTools, we could accidently prevent React DevTools from installing:
80
94
// https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-devtools-extensions/src/contentScripts/installHook.js#L30C6-L30C27
@@ -110,7 +124,6 @@ export const patchRDTHook = (onActive?: () => unknown): void => {
110
124
const rdtHook = globalThis . __REACT_DEVTOOLS_GLOBAL_HOOK__ ;
111
125
if ( ! rdtHook ) return ;
112
126
if ( ! rdtHook . _instrumentationSource ) {
113
- isReactRefreshOverride = isReactRefresh ( rdtHook ) ;
114
127
rdtHook . checkDCE = checkDCE ;
115
128
rdtHook . supportsFiber = true ;
116
129
rdtHook . supportsFlight = true ;
@@ -128,21 +141,21 @@ export const patchRDTHook = (onActive?: () => unknown): void => {
128
141
isReactRefreshOverride = true ;
129
142
// but since the underlying implementation doens't care,
130
143
// it's ok: https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
131
- // @ts -expect-error this is not actually a ReactRenderer,
132
- let nextID = rdtHook . inject ( null ) ;
144
+ const nextID = rdtHook . inject ( {
145
+ // @ts -expect-error this is not actually a ReactRenderer,
146
+ scheduleRefresh ( ) { } ,
147
+ } ) ;
133
148
if ( nextID ) {
134
149
rdtHook . _instrumentationIsActive = true ;
135
150
}
136
- rdtHook . inject = ( ) => nextID ++ ;
137
- } else {
138
- rdtHook . inject = ( renderer ) => {
139
- const id = prevInject ( renderer ) ;
140
- rdtHook . _instrumentationIsActive = true ;
141
- // biome-ignore lint/complexity/noForEach: prefer forEach for Set
142
- onActiveListeners . forEach ( ( listener ) => listener ( ) ) ;
143
- return id ;
144
- } ;
145
151
}
152
+ rdtHook . inject = ( renderer ) => {
153
+ const id = prevInject ( renderer ) ;
154
+ rdtHook . _instrumentationIsActive = true ;
155
+ // biome-ignore lint/complexity/noForEach: prefer forEach for Set
156
+ onActiveListeners . forEach ( ( listener ) => listener ( ) ) ;
157
+ return id ;
158
+ } ;
146
159
}
147
160
if (
148
161
rdtHook . renderers . size ||
@@ -183,3 +196,15 @@ export const isClientEnvironment = (): boolean => {
183
196
window . navigator ?. product === 'ReactNative' ) ,
184
197
) ;
185
198
} ;
199
+
200
+ /**
201
+ * Usually used purely for side effect
202
+ */
203
+ export const safelyInstallRDTHook = ( ) => {
204
+ try {
205
+ // __REACT_DEVTOOLS_GLOBAL_HOOK__ must exist before React is ever executed
206
+ if ( isClientEnvironment ( ) ) {
207
+ getRDTHook ( ) ;
208
+ }
209
+ } catch { }
210
+ } ;
0 commit comments