Patch devtools before running useMemo function in strict mode (#28249)
This fixes a regression https://github.com/facebook/react/pull/25583 where we stopped patching before calling useMemo function. Fixes https://github.com/facebook/react/issues/27989
Sathya Gunasekaran committed
Feb 6, 2024 at 16:45 UTC
db120f69ec7a0b8c7f38ca7a1ddb1886de92e465
2 files changed
+147
-2
packages/react-devtools-shared/src/__tests__/console-test.js
+141
@@ -625,6 +625,147 @@ describe('console', () => {
625
expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
626
});
627
628
+ it('should double log from useMemo if hideConsoleLogsInStrictMode is disabled in Strict mode', () => {
629
+ global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
630
+ global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = false;
631
+
632
+ const container = document.createElement('div');
633
+ const root = ReactDOMClient.createRoot(container);
634
+
635
+ function App() {
636
+ React.useMemo(() => {
637
+ fakeConsole.log('log');
638
+ fakeConsole.warn('warn');
639
+ fakeConsole.error('error');
640
+ fakeConsole.info('info');
641
+ fakeConsole.group('group');
642
+ fakeConsole.groupCollapsed('groupCollapsed');
643
+ }, []);
644
+ return <div />;
645
+ }
646
+
647
+ act(() =>
648
+ root.render(
649
+ <React.StrictMode>
650
+ <App />
651
+ </React.StrictMode>,
652
+ ),
653
+ );
654
+ expect(mockLog.mock.calls[0]).toHaveLength(1);
655
+ expect(mockLog.mock.calls[0][0]).toBe('log');
656
+ expect(mockLog.mock.calls[1]).toEqual([
657
+ '%c%s',
658
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
659
+ 'log',
660
+ ]);
661
+
662
+ expect(mockWarn).toHaveBeenCalledTimes(2);
663
+ expect(mockWarn.mock.calls[0]).toHaveLength(1);
664
+ expect(mockWarn.mock.calls[0][0]).toBe('warn');
665
+ expect(mockWarn.mock.calls[1]).toHaveLength(3);
666
+ expect(mockWarn.mock.calls[1]).toEqual([
667
+ '%c%s',
668
+ `color: ${process.env.DARK_MODE_DIMMED_WARNING_COLOR}`,
669
+ 'warn',
670
+ ]);
671
+
672
+ expect(mockError).toHaveBeenCalledTimes(2);
673
+ expect(mockError.mock.calls[0]).toHaveLength(1);
674
+ expect(mockError.mock.calls[0][0]).toBe('error');
675
+ expect(mockError.mock.calls[1]).toHaveLength(3);
676
+ expect(mockError.mock.calls[1]).toEqual([
677
+ '%c%s',
678
+ `color: ${process.env.DARK_MODE_DIMMED_ERROR_COLOR}`,
679
+ 'error',
680
+ ]);
681
+
682
+ expect(mockInfo).toHaveBeenCalledTimes(2);
683
+ expect(mockInfo.mock.calls[0]).toHaveLength(1);
684
+ expect(mockInfo.mock.calls[0][0]).toBe('info');
685
+ expect(mockInfo.mock.calls[1]).toHaveLength(3);
686
+ expect(mockInfo.mock.calls[1]).toEqual([
687
+ '%c%s',
688
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
689
+ 'info',
690
+ ]);
691
+
692
+ expect(mockGroup).toHaveBeenCalledTimes(2);
693
+ expect(mockGroup.mock.calls[0]).toHaveLength(1);
694
+ expect(mockGroup.mock.calls[0][0]).toBe('group');
695
+ expect(mockGroup.mock.calls[1]).toHaveLength(3);
696
+ expect(mockGroup.mock.calls[1]).toEqual([
697
+ '%c%s',
698
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
699
+ 'group',
700
+ ]);
701
+
702
+ expect(mockGroupCollapsed).toHaveBeenCalledTimes(2);
703
+ expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
704
+ expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
705
+ expect(mockGroupCollapsed.mock.calls[1]).toHaveLength(3);
706
+ expect(mockGroupCollapsed.mock.calls[1]).toEqual([
707
+ '%c%s',
708
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
709
+ 'groupCollapsed',
710
+ ]);
711
+ });
712
+
713
+ it('should not double log from useMemo fns if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
714
+ global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
715
+ global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = true;
716
+
717
+ const container = document.createElement('div');
718
+ const root = ReactDOMClient.createRoot(container);
719
+
720
+ function App() {
721
+ React.useMemo(() => {
722
+ console.log(
723
+ 'CALL',
724
+ global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__,
725
+ );
726
+ fakeConsole.log('log');
727
+ fakeConsole.warn('warn');
728
+ fakeConsole.error('error');
729
+ fakeConsole.info('info');
730
+ fakeConsole.group('group');
731
+ fakeConsole.groupCollapsed('groupCollapsed');
732
+ }, []);
733
+ return <div />;
734
+ }
735
+
736
+ act(() =>
737
+ root.render(
738
+ <React.StrictMode>
739
+ <App />
740
+ </React.StrictMode>,
741
+ ),
742
+ );
743
+
744
+ expect(mockLog).toHaveBeenCalledTimes(1);
745
+ expect(mockLog.mock.calls[0]).toHaveLength(1);
746
+ expect(mockLog.mock.calls[0][0]).toBe('log');
747
+
748
+ expect(mockWarn).toHaveBeenCalledTimes(1);
749
+ expect(mockWarn.mock.calls[0]).toHaveLength(1);
750
+ expect(mockWarn.mock.calls[0][0]).toBe('warn');
751
+
752
+ expect(mockError).toHaveBeenCalledTimes(1);
753
+ expect(mockError.mock.calls[0]).toHaveLength(1);
754
+ expect(mockError.mock.calls[0][0]).toBe('error');
755
+
756
+ expect(mockInfo).toHaveBeenCalledTimes(1);
757
+ expect(mockInfo.mock.calls[0]).toHaveLength(1);
758
+ expect(mockInfo.mock.calls[0][0]).toBe('info');
759
+
760
+ expect(mockGroup).toHaveBeenCalledTimes(1);
761
+ expect(mockGroup.mock.calls[0]).toHaveLength(1);
762
+ expect(mockGroup.mock.calls[0][0]).toBe('group');
763
+
764
+ expect(mockGroupCollapsed).toHaveBeenCalledTimes(1);
765
+ expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
766
+ expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
767
+ });
768
+
769
it('should double log in Strict mode initial render for extension', () => {
770
global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
771
global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = false;
packages/react-reconciler/src/ReactFiberHooks.js
+6
-2
@@ -2636,10 +2636,12 @@ function mountMemo<T>(
2636
): T {
2637
const hook = mountWorkInProgressHook();
2638
const nextDeps = deps === undefined ? null : deps;
2639
+ const nextValue = nextCreate();
2640
if (shouldDoubleInvokeUserFnsInHooksDEV) {
2641
+ setIsStrictModeForDevtools(true);
2642
nextCreate();
2643
+ setIsStrictModeForDevtools(false);
2644
}
2642
- const nextValue = nextCreate();
2645
hook.memoizedState = [nextValue, nextDeps];
2646
return nextValue;
2647
}
@@ -2658,10 +2660,12 @@ function updateMemo<T>(
2660
return prevState[0];
2661
}
2662
}
2663
+ const nextValue = nextCreate();
2664
if (shouldDoubleInvokeUserFnsInHooksDEV) {
2665
+ setIsStrictModeForDevtools(true);
2666
nextCreate();
2667
+ setIsStrictModeForDevtools(false);
2668
}
2664
- const nextValue = nextCreate();
2669
hook.memoizedState = [nextValue, nextDeps];
2670
return nextValue;
2671
}