@samitouri / QOS-React / commits / db120f69ec

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 }