Repository: facebook/react
Pull request: Don't warn about concurrently rendering contexts if we finished rendering

## Summary

Closes https://github.com/facebook/react/issues/22796

## How did you test this change?

- yarn test ReactFiberNewContext
- [x] CI
- ~[ ] codesandbox of linked issue~ Can't test codesandbox since `react-test-renderer` isn't built by Codesandbox CI. Though I tested it locally by adding `context._currentRenderer2 = null;` to `popProvider` in `react-test-renderer.development.js`.


Changed files:
filename: packages/react-reconciler/src/ReactFiberNewContext.js
patch: @@ -49,6 +49,15 @@ import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols
 
 const valueCursor: StackCursor<mixed> = createCursor(null);
 
+let rendererCursorDEV: StackCursor<Object | null>;
+if (__DEV__) {
+  rendererCursorDEV = createCursor(null);
+}
+let renderer2CursorDEV: StackCursor<Object | null>;
+if (__DEV__) {
+  renderer2CursorDEV = createCursor(null);
+}
+
 let rendererSigil;
 if (__DEV__) {
   // Use this to detect multiple renderers using the same context
@@ -94,6 +103,8 @@ export function pushProvider<T>(
 
     context._currentValue = nextValue;
     if (__DEV__) {
+      push(rendererCursorDEV, context._currentRenderer, providerFiber);
+
       if (
         context._currentRenderer !== undefined &&
         context._currentRenderer !== null &&
@@ -111,6 +122,8 @@ export function pushProvider<T>(
 
     context._currentValue2 = nextValue;
     if (__DEV__) {
+      push(renderer2CursorDEV, context._currentRenderer2, providerFiber);
+
       if (
         context._currentRenderer2 !== undefined &&
         context._currentRenderer2 !== null &&
@@ -131,7 +144,7 @@ export function popProvider(
   providerFiber: Fiber,
 ): void {
   const currentValue = valueCursor.current;
-  pop(valueCursor, providerFiber);
+
   if (isPrimaryRenderer) {
     if (
       enableServerContext &&
@@ -141,6 +154,11 @@ export function popProvider(
     } else {
       context._currentValue = currentValue;
     }
+    if (__DEV__) {
+      const currentRenderer = rendererCursorDEV.current;
+      pop(rendererCursorDEV, providerFiber);
+      context._currentRenderer = currentRenderer;
+    }
   } else {
     if (
       enableServerContext &&
@@ -150,7 +168,14 @@ export function popProvider(
     } else {
       context._currentValue2 = currentValue;
     }
+    if (__DEV__) {
+      const currentRenderer2 = renderer2CursorDEV.current;
+      pop(renderer2CursorDEV, providerFiber);
+      context._currentRenderer2 = currentRenderer2;
+    }
   }
+
+  pop(valueCursor, providerFiber);
 }
 
 export function scheduleContextWorkOnParentPath(

filename: packages/react-reconciler/src/__tests__/ReactNewContext-test.js
patch: @@ -874,6 +874,48 @@ describe('ReactNewContext', () => {
       }
     });
 
+    it('does not warn if multiple renderers use the same context sequentially', () => {
+      spyOnDev(console, 'error');
+      const Context = React.createContext(0);
+
+      function Foo(props) {
+        Scheduler.unstable_yieldValue('Foo');
+        return null;
+      }
+
+      function App(props) {
+        return (
+          <Context.Provider value={props.value}>
+            <Foo />
+            <Foo />
+          </Context.Provider>
+        );
+      }
+
+      if (gate(flags => flags.enableSyncDefaultUpdates)) {
+        React.startTransition(() => {
+          ReactNoop.render(<App value={1} />);
+        });
+      } else {
+        ReactNoop.render(<App value={1} />);
+      }
+      expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
+
+      // Get a new copy of ReactNoop
+      jest.resetModules();
+      React = require('react');
+      ReactNoop = require('react-noop-renderer');
+      Scheduler = require('scheduler');
+
+      // Render the provider again using a different renderer
+      ReactNoop.render(<App value={1} />);
+      expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
+
+      if (__DEV__) {
+        expect(console.error).not.toHaveBeenCalled();
+      }
+    });
+
     it('provider bails out if children and value are unchanged (like sCU)', () => {
       const Context = React.createContext(0);