Skip to content

Commit

Permalink
add tests capturing effects behavior for legacy and modern strict mod…
Browse files Browse the repository at this point in the history
…e during fast refresh
  • Loading branch information
noahlemen committed Apr 12, 2024
1 parent 96c5846 commit 944b930
Showing 1 changed file with 124 additions and 1 deletion.
125 changes: 124 additions & 1 deletion packages/react-refresh/src/__tests__/ReactFresh-test.js
Expand Up @@ -51,7 +51,7 @@ describe('ReactFresh', () => {
}
});

afterEach(() => {
afterEach(async () => {
if (__DEV__) {
delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
document.body.removeChild(container);
Expand Down Expand Up @@ -3813,6 +3813,129 @@ describe('ReactFresh', () => {
}
});

it('double invokes effects in StrictMode when remounting', async () => {
if (__DEV__) {
await render(() => {
function App() {
React.useEffect(() => {
Scheduler.log('useEffect mount');
return () => Scheduler.log('useEffect unmount');
});

React.useLayoutEffect(() => {
Scheduler.log('useLayoutEffect mount');
return () => Scheduler.log('useLayoutEffect unmount');
});

return null;
}
$RefreshReg$(App, 'App');
$RefreshSig$(App, '1');
return () => (
<React.StrictMode>
<App />
</React.StrictMode>
);
});

if (gate(flags => flags.useModernStrictMode)) {
// this seems contrary to observed behavior in practice - unclear why effects are not double-invoked here
// effects should be double invoked here in modern strict mode
assertLog(['useLayoutEffect mount', 'useEffect mount']);
} else {
// in legacy strict mode, effects are double invoked on initial render
assertLog([
'useLayoutEffect mount',
'useEffect mount',
'useLayoutEffect unmount',
'useEffect unmount',
'useLayoutEffect mount',
'useEffect mount',
]);
}

// Refresh without changing signature
await patch(() => {
function App() {
React.useEffect(() => {
Scheduler.log('useEffect mount');
return () => Scheduler.log('useEffect unmount');
});

React.useLayoutEffect(() => {
Scheduler.log('useLayoutEffect mount');
return () => Scheduler.log('useLayoutEffect unmount');
});

return null;
}
$RefreshReg$(App, 'App');
$RefreshSig$(App, '1');
return App;
});

// no difference between modern and legacy strict mode here -- we unmount and remount effects
if (gate(flags => flags.useModernStrictMode)) {
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
'useEffect mount',
]);
} else {
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
'useEffect mount',
]);
}

// Refresh with change to signature
await patch(() => {
function App() {
React.useEffect(() => {
Scheduler.log('useEffect mount');
return () => Scheduler.log('useEffect unmount');
});

React.useLayoutEffect(() => {
Scheduler.log('useLayoutEffect mount');
return () => Scheduler.log('useLayoutEffect unmount');
});

return null;
}
$RefreshReg$(App, 'App');
$RefreshSig$(App, '2');
return App;
});

if (gate(flags => flags.useModernStrictMode)) {
// in modern strict mode, we unmount and remount like above, but do not double-invoke effects
// we probably should be double invoking effects?
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
'useEffect mount',
]);
} else {
// in legacy strict mode, we unmount, remount, and then unmount and remount once more for double-invocation
assertLog([
'useLayoutEffect unmount',
'useLayoutEffect mount',
'useEffect unmount',
'useEffect mount',
'useLayoutEffect unmount',
'useEffect unmount',
'useLayoutEffect mount',
'useEffect mount',
]);
}
}
});

function initFauxDevToolsHook() {
const onCommitFiberRoot = jest.fn();
const onCommitFiberUnmount = jest.fn();
Expand Down

0 comments on commit 944b930

Please sign in to comment.