Don't warn about concurrently rendering contexts if we finished rendering (#22797)
Closes https://github.com/facebook/react/issues/22796
Sebastian Silbermann committed
Jan 12, 2023 at 13:17 UTC
555ece0cd14779abd5a1fc50f71625f9ada42bef
2 files changed
+68
-1
packages/react-reconciler/src/ReactFiberNewContext.js
+26
-1
@@ -49,6 +49,15 @@ import {REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED} from 'shared/ReactSymbols
49
50
const valueCursor: StackCursor<mixed> = createCursor(null);
51
52
+let rendererCursorDEV: StackCursor<Object | null>;
53
+if (__DEV__) {
54
+ rendererCursorDEV = createCursor(null);
55
+}
56
+let renderer2CursorDEV: StackCursor<Object | null>;
57
+if (__DEV__) {
58
+ renderer2CursorDEV = createCursor(null);
59
+}
60
+
61
let rendererSigil;
62
if (__DEV__) {
63
// Use this to detect multiple renderers using the same context
@@ -94,6 +103,8 @@ export function pushProvider<T>(
103
104
context._currentValue = nextValue;
105
if (__DEV__) {
106
+ push(rendererCursorDEV, context._currentRenderer, providerFiber);
107
+
108
if (
109
context._currentRenderer !== undefined &&
110
context._currentRenderer !== null &&
@@ -111,6 +122,8 @@ export function pushProvider<T>(
122
123
context._currentValue2 = nextValue;
124
if (__DEV__) {
125
+ push(renderer2CursorDEV, context._currentRenderer2, providerFiber);
126
+
127
if (
128
context._currentRenderer2 !== undefined &&
129
context._currentRenderer2 !== null &&
@@ -131,7 +144,7 @@ export function popProvider(
144
providerFiber: Fiber,
145
): void {
146
const currentValue = valueCursor.current;
134
- pop(valueCursor, providerFiber);
147
+
148
if (isPrimaryRenderer) {
149
if (
150
enableServerContext &&
@@ -141,6 +154,11 @@ export function popProvider(
154
} else {
155
context._currentValue = currentValue;
156
}
157
+ if (__DEV__) {
158
+ const currentRenderer = rendererCursorDEV.current;
159
+ pop(rendererCursorDEV, providerFiber);
160
+ context._currentRenderer = currentRenderer;
161
+ }
162
} else {
163
if (
164
enableServerContext &&
@@ -150,7 +168,14 @@ export function popProvider(
168
} else {
169
context._currentValue2 = currentValue;
170
}
171
+ if (__DEV__) {
172
+ const currentRenderer2 = renderer2CursorDEV.current;
173
+ pop(renderer2CursorDEV, providerFiber);
174
+ context._currentRenderer2 = currentRenderer2;
175
+ }
176
}
177
+
178
+ pop(valueCursor, providerFiber);
179
}
180
181
export function scheduleContextWorkOnParentPath(
packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+42
@@ -874,6 +874,48 @@ describe('ReactNewContext', () => {
874
}
875
});
876
877
+ it('does not warn if multiple renderers use the same context sequentially', () => {
878
+ spyOnDev(console, 'error');
879
+ const Context = React.createContext(0);
880
+
881
+ function Foo(props) {
882
+ Scheduler.unstable_yieldValue('Foo');
883
+ return null;
884
+ }
885
+
886
+ function App(props) {
887
+ return (
888
+ <Context.Provider value={props.value}>
889
+ <Foo />
890
+ <Foo />
891
+ </Context.Provider>
892
+ );
893
+ }
894
+
895
+ if (gate(flags => flags.enableSyncDefaultUpdates)) {
896
+ React.startTransition(() => {
897
+ ReactNoop.render(<App value={1} />);
898
+ });
899
+ } else {
900
+ ReactNoop.render(<App value={1} />);
901
+ }
902
+ expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
903
+
904
+ // Get a new copy of ReactNoop
905
+ jest.resetModules();
906
+ React = require('react');
907
+ ReactNoop = require('react-noop-renderer');
908
+ Scheduler = require('scheduler');
909
+
910
+ // Render the provider again using a different renderer
911
+ ReactNoop.render(<App value={1} />);
912
+ expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
913
+
914
+ if (__DEV__) {
915
+ expect(console.error).not.toHaveBeenCalled();
916
+ }
917
+ });
918
+
919
it('provider bails out if children and value are unchanged (like sCU)', () => {
920
const Context = React.createContext(0);
921