@@ -42,6 +42,7 @@ describe('ReactFresh', () => {
4242
4343 afterEach ( ( ) => {
4444 if ( __DEV__ ) {
45+ delete global . __REACT_DEVTOOLS_GLOBAL_HOOK__ ;
4546 document . body . removeChild ( container ) ;
4647 }
4748 } ) ;
@@ -3707,4 +3708,80 @@ describe('ReactFresh', () => {
37073708 // For example, we can use this to print a log of what was updated.
37083709 }
37093710 } ) ;
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+ } ) ;
37103787} ) ;
0 commit comments