Repository: facebook/react
Pull request: Patch devtools before running useMemo function in strict mode

## Summary

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


## Test
` yarn test --build --project=devtools --ci`

Changed files:
filename: packages/react-devtools-shared/src/__tests__/console-test.js
patch: @@ -625,6 +625,147 @@ describe('console', () => {
     expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
   });
 
+  it('should double log from useMemo if hideConsoleLogsInStrictMode is disabled in Strict mode', () => {
+    global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
+    global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = false;
+
+    const container = document.createElement('div');
+    const root = ReactDOMClient.createRoot(container);
+
+    function App() {
+      React.useMemo(() => {
+        fakeConsole.log('log');
+        fakeConsole.warn('warn');
+        fakeConsole.error('error');
+        fakeConsole.info('info');
+        fakeConsole.group('group');
+        fakeConsole.groupCollapsed('groupCollapsed');
+      }, []);
+      return <div />;
+    }
+
+    act(() =>
+      root.render(
+        <React.StrictMode>
+          <App />
+        </React.StrictMode>,
+      ),
+    );
+    expect(mockLog.mock.calls[0]).toHaveLength(1);
+    expect(mockLog.mock.calls[0][0]).toBe('log');
+    expect(mockLog.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
+      'log',
+    ]);
+
+    expect(mockWarn).toHaveBeenCalledTimes(2);
+    expect(mockWarn.mock.calls[0]).toHaveLength(1);
+    expect(mockWarn.mock.calls[0][0]).toBe('warn');
+    expect(mockWarn.mock.calls[1]).toHaveLength(3);
+    expect(mockWarn.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_WARNING_COLOR}`,
+      'warn',
+    ]);
+
+    expect(mockError).toHaveBeenCalledTimes(2);
+    expect(mockError.mock.calls[0]).toHaveLength(1);
+    expect(mockError.mock.calls[0][0]).toBe('error');
+    expect(mockError.mock.calls[1]).toHaveLength(3);
+    expect(mockError.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_ERROR_COLOR}`,
+      'error',
+    ]);
+
+    expect(mockInfo).toHaveBeenCalledTimes(2);
+    expect(mockInfo.mock.calls[0]).toHaveLength(1);
+    expect(mockInfo.mock.calls[0][0]).toBe('info');
+    expect(mockInfo.mock.calls[1]).toHaveLength(3);
+    expect(mockInfo.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
+      'info',
+    ]);
+
+    expect(mockGroup).toHaveBeenCalledTimes(2);
+    expect(mockGroup.mock.calls[0]).toHaveLength(1);
+    expect(mockGroup.mock.calls[0][0]).toBe('group');
+    expect(mockGroup.mock.calls[1]).toHaveLength(3);
+    expect(mockGroup.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
+      'group',
+    ]);
+
+    expect(mockGroupCollapsed).toHaveBeenCalledTimes(2);
+    expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
+    expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
+    expect(mockGroupCollapsed.mock.calls[1]).toHaveLength(3);
+    expect(mockGroupCollapsed.mock.calls[1]).toEqual([
+      '%c%s',
+      `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
+      'groupCollapsed',
+    ]);
+  });
+
+  it('should not double log from useMemo fns if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
+    global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
+    global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = true;
+
+    const container = document.createElement('div');
+    const root = ReactDOMClient.createRoot(container);
+
+    function App() {
+      React.useMemo(() => {
+        console.log(
+          'CALL',
+          global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__,
+        );
+        fakeConsole.log('log');
+        fakeConsole.warn('warn');
+        fakeConsole.error('error');
+        fakeConsole.info('info');
+        fakeConsole.group('group');
+        fakeConsole.groupCollapsed('groupCollapsed');
+      }, []);
+      return <div />;
+    }
+
+    act(() =>
+      root.render(
+        <React.StrictMode>
+          <App />
+        </React.StrictMode>,
+      ),
+    );
+
+    expect(mockLog).toHaveBeenCalledTimes(1);
+    expect(mockLog.mock.calls[0]).toHaveLength(1);
+    expect(mockLog.mock.calls[0][0]).toBe('log');
+
+    expect(mockWarn).toHaveBeenCalledTimes(1);
+    expect(mockWarn.mock.calls[0]).toHaveLength(1);
+    expect(mockWarn.mock.calls[0][0]).toBe('warn');
+
+    expect(mockError).toHaveBeenCalledTimes(1);
+    expect(mockError.mock.calls[0]).toHaveLength(1);
+    expect(mockError.mock.calls[0][0]).toBe('error');
+
+    expect(mockInfo).toHaveBeenCalledTimes(1);
+    expect(mockInfo.mock.calls[0]).toHaveLength(1);
+    expect(mockInfo.mock.calls[0][0]).toBe('info');
+
+    expect(mockGroup).toHaveBeenCalledTimes(1);
+    expect(mockGroup.mock.calls[0]).toHaveLength(1);
+    expect(mockGroup.mock.calls[0][0]).toBe('group');
+
+    expect(mockGroupCollapsed).toHaveBeenCalledTimes(1);
+    expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
+    expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
+  });
+
   it('should double log in Strict mode initial render for extension', () => {
     global.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = false;
     global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = false;

filename: packages/react-reconciler/src/ReactFiberHooks.js
patch: @@ -2636,10 +2636,12 @@ function mountMemo<T>(
 ): T {
   const hook = mountWorkInProgressHook();
   const nextDeps = deps === undefined ? null : deps;
+  const nextValue = nextCreate();
   if (shouldDoubleInvokeUserFnsInHooksDEV) {
+    setIsStrictModeForDevtools(true);
     nextCreate();
+    setIsStrictModeForDevtools(false);
   }
-  const nextValue = nextCreate();
   hook.memoizedState = [nextValue, nextDeps];
   return nextValue;
 }
@@ -2658,10 +2660,12 @@ function updateMemo<T>(
       return prevState[0];
     }
   }
+  const nextValue = nextCreate();
   if (shouldDoubleInvokeUserFnsInHooksDEV) {
+    setIsStrictModeForDevtools(true);
     nextCreate();
+    setIsStrictModeForDevtools(false);
   }
-  const nextValue = nextCreate();
   hook.memoizedState = [nextValue, nextDeps];
   return nextValue;
 }