@@ -42,6 +42,7 @@ describe('ReactFresh', () => {
42
42
43
43
afterEach ( ( ) => {
44
44
if ( __DEV__ ) {
45
+ delete global . __REACT_DEVTOOLS_GLOBAL_HOOK__ ;
45
46
document . body . removeChild ( container ) ;
46
47
}
47
48
} ) ;
@@ -3707,4 +3708,80 @@ describe('ReactFresh', () => {
3707
3708
// For example, we can use this to print a log of what was updated.
3708
3709
}
3709
3710
} ) ;
3711
+
3712
+ // This simulates the scenario in https://github.com/facebook/react/issues/17626.
3713
+ it ( 'can inject the runtime after the renderer executes' , ( ) => {
3714
+ if ( __DEV__ ) {
3715
+ // This is a minimal shim for the global hook installed by DevTools.
3716
+ // The real one is in packages/react-devtools-shared/src/hook.js.
3717
+ let idCounter = 0 ;
3718
+ let renderers = new Map ( ) ;
3719
+ global . __REACT_DEVTOOLS_GLOBAL_HOOK__ = {
3720
+ renderers,
3721
+ supportsFiber : true ,
3722
+ inject ( renderer ) {
3723
+ const id = ++ idCounter ;
3724
+ renderers . set ( id , renderer ) ;
3725
+ return id ;
3726
+ } ,
3727
+ onCommitFiberRoot ( ) { } ,
3728
+ onCommitFiberUnmount ( ) { } ,
3729
+ } ;
3730
+
3731
+ // Load these first, as if they're coming from a CDN.
3732
+ jest . resetModules ( ) ;
3733
+ React = require ( 'react' ) ;
3734
+ ReactDOM = require ( 'react-dom' ) ;
3735
+ Scheduler = require ( 'scheduler' ) ;
3736
+ act = require ( 'react-dom/test-utils' ) . act ;
3737
+
3738
+ // Important! Inject into the global hook *after* ReactDOM runs:
3739
+ ReactFreshRuntime = require ( 'react-refresh/runtime' ) ;
3740
+ ReactFreshRuntime . injectIntoGlobalHook ( global ) ;
3741
+
3742
+ // We're verifying that we're able to track roots mounted after this point.
3743
+ // The rest of this test is taken from the simplest first test case.
3744
+
3745
+ render ( ( ) => {
3746
+ function Hello ( ) {
3747
+ const [ val , setVal ] = React . useState ( 0 ) ;
3748
+ return (
3749
+ < p style = { { color : 'blue' } } onClick = { ( ) => setVal ( val + 1 ) } >
3750
+ { val }
3751
+ </ p >
3752
+ ) ;
3753
+ }
3754
+ $RefreshReg$ ( Hello , 'Hello' ) ;
3755
+ return Hello ;
3756
+ } ) ;
3757
+
3758
+ // Bump the state before patching.
3759
+ const el = container . firstChild ;
3760
+ expect ( el . textContent ) . toBe ( '0' ) ;
3761
+ expect ( el . style . color ) . toBe ( 'blue' ) ;
3762
+ act ( ( ) => {
3763
+ el . dispatchEvent ( new MouseEvent ( 'click' , { bubbles : true } ) ) ;
3764
+ } ) ;
3765
+ expect ( el . textContent ) . toBe ( '1' ) ;
3766
+
3767
+ // Perform a hot update.
3768
+ patch ( ( ) => {
3769
+ function Hello ( ) {
3770
+ const [ val , setVal ] = React . useState ( 0 ) ;
3771
+ return (
3772
+ < p style = { { color : 'red' } } onClick = { ( ) => setVal ( val + 1 ) } >
3773
+ { val }
3774
+ </ p >
3775
+ ) ;
3776
+ }
3777
+ $RefreshReg$ ( Hello , 'Hello' ) ;
3778
+ return Hello ;
3779
+ } ) ;
3780
+
3781
+ // Assert the state was preserved but color changed.
3782
+ expect ( container . firstChild ) . toBe ( el ) ;
3783
+ expect ( el . textContent ) . toBe ( '1' ) ;
3784
+ expect ( el . style . color ) . toBe ( 'red' ) ;
3785
+ }
3786
+ } ) ;
3710
3787
} ) ;
0 commit comments