Switch to JSX API for context (#12123)
* Switch to JSX API for context 80% sure this will be the final API. Merging this now so we can get this into the next www sync in preparation for 16.3. * Promote context to a stable API
Andrew Clark committed
Jan 30, 2018 at 13:06 UTC
28aa084ad84a681be1c45def2f4b4c0dd8a43871
13 files changed
+326
-350
packages/react-dom/src/__tests__/ReactServerRendering-test.internal.js
+20
-31
@@ -14,7 +14,6 @@ let React;
14
let ReactCallReturn;
15
let ReactDOMServer;
16
let PropTypes;
17
-let ReactFeatureFlags;
17
18
function normalizeCodeLocInfo(str) {
19
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -23,8 +22,6 @@ function normalizeCodeLocInfo(str) {
22
describe('ReactDOMServer', () => {
23
beforeEach(() => {
24
jest.resetModules();
26
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
27
- ReactFeatureFlags.enableNewContextAPI = true;
25
React = require('react');
26
ReactCallReturn = require('react-call-return');
27
PropTypes = require('prop-types');
@@ -388,36 +385,32 @@ describe('ReactDOMServer', () => {
385
});
386
387
it('renders with new context API', () => {
391
- const Context = React.unstable_createContext(0);
392
-
393
- function Provider(props) {
394
- return Context.provide(props.value, props.children);
395
- }
388
+ const Context = React.createContext(0);
389
390
function Consumer(props) {
398
- return Context.consume(value => {
399
- return 'Result: ' + value;
400
- });
391
+ return (
392
+ <Context.Consumer>{value => 'Result: ' + value}</Context.Consumer>
393
+ );
394
}
395
396
const Indirection = React.Fragment;
397
398
function App(props) {
399
return (
407
- <Provider value={props.value}>
408
- <Provider value={2}>
400
+ <Context.Provider value={props.value}>
401
+ <Context.Provider value={2}>
402
<Consumer />
410
- </Provider>
403
+ </Context.Provider>
404
<Indirection>
405
<Indirection>
406
<Consumer />
414
- <Provider value={3}>
407
+ <Context.Provider value={3}>
408
<Consumer />
416
- </Provider>
409
+ </Context.Provider>
410
</Indirection>
411
</Indirection>
412
<Consumer />
420
- </Provider>
413
+ </Context.Provider>
414
);
415
}
416
@@ -428,16 +421,12 @@ describe('ReactDOMServer', () => {
421
});
422
423
it('renders context API, reentrancy', () => {
431
- const Context = React.unstable_createContext(0);
432
-
433
- function Provider(props) {
434
- return Context.provide(props.value, props.children);
435
- }
424
+ const Context = React.createContext(0);
425
426
function Consumer(props) {
438
- return Context.consume(value => {
439
- return 'Result: ' + value;
440
- });
427
+ return (
428
+ <Context.Consumer>{value => 'Result: ' + value}</Context.Consumer>
429
+ );
430
}
431
432
let reentrantMarkup;
@@ -452,21 +441,21 @@ describe('ReactDOMServer', () => {
441
442
function App(props) {
443
return (
455
- <Provider value={props.value}>
444
+ <Context.Provider value={props.value}>
445
{props.reentrant && <Reentrant />}
457
- <Provider value={2}>
446
+ <Context.Provider value={2}>
447
<Consumer />
459
- </Provider>
448
+ </Context.Provider>
449
<Indirection>
450
<Indirection>
451
<Consumer />
463
- <Provider value={3}>
452
+ <Context.Provider value={3}>
453
<Consumer />
465
- </Provider>
454
+ </Context.Provider>
455
</Indirection>
456
</Indirection>
457
<Consumer />
469
- </Provider>
458
+ </Context.Provider>
459
);
460
}
461
packages/react-dom/src/server/ReactPartialRenderer.js
+2
-2
@@ -860,10 +860,10 @@ class ReactDOMServerRenderer {
860
}
861
case REACT_CONTEXT_TYPE: {
862
const consumer: ReactConsumer<any> = (nextChild: any);
863
- const nextProps = consumer.props;
863
+ const nextProps: any = consumer.props;
864
const nextValue = consumer.type.currentValue;
865
866
- const nextChildren = toArray(nextProps.render(nextValue));
866
+ const nextChildren = toArray(nextProps.children(nextValue));
867
const frame: Frame = {
868
type: nextChild,
869
domNamespace: parentNamespace,
packages/react-reconciler/src/ReactFiberBeginWork.js
+150
-160
@@ -15,7 +15,6 @@ import type {HydrationContext} from './ReactFiberHydrationContext';
15
import type {FiberRoot} from './ReactFiberRoot';
16
import type {ExpirationTime} from './ReactFiberExpirationTime';
17
18
-import {enableNewContextAPI} from 'shared/ReactFeatureFlags';
18
import {
19
IndeterminateComponent,
20
FunctionalComponent,
@@ -678,87 +677,85 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
677
changedBits: number,
678
renderExpirationTime: ExpirationTime,
679
): void {
681
- if (enableNewContextAPI) {
682
- let fiber = workInProgress.child;
683
- while (fiber !== null) {
684
- let nextFiber;
685
- // Visit this fiber.
686
- switch (fiber.tag) {
687
- case ContextConsumer:
688
- // Check if the context matches.
689
- const observedBits: number = fiber.stateNode | 0;
690
- if (fiber.type === context && (observedBits & changedBits) !== 0) {
691
- // Update the expiration time of all the ancestors, including
692
- // the alternates.
693
- let node = fiber;
694
- while (node !== null) {
695
- const alternate = node.alternate;
680
+ let fiber = workInProgress.child;
681
+ while (fiber !== null) {
682
+ let nextFiber;
683
+ // Visit this fiber.
684
+ switch (fiber.tag) {
685
+ case ContextConsumer:
686
+ // Check if the context matches.
687
+ const observedBits: number = fiber.stateNode | 0;
688
+ if (fiber.type === context && (observedBits & changedBits) !== 0) {
689
+ // Update the expiration time of all the ancestors, including
690
+ // the alternates.
691
+ let node = fiber;
692
+ while (node !== null) {
693
+ const alternate = node.alternate;
694
+ if (
695
+ node.expirationTime === NoWork ||
696
+ node.expirationTime > renderExpirationTime
697
+ ) {
698
+ node.expirationTime = renderExpirationTime;
699
if (
697
- node.expirationTime === NoWork ||
698
- node.expirationTime > renderExpirationTime
699
- ) {
700
- node.expirationTime = renderExpirationTime;
701
- if (
702
- alternate !== null &&
703
- (alternate.expirationTime === NoWork ||
704
- alternate.expirationTime > renderExpirationTime)
705
- ) {
706
- alternate.expirationTime = renderExpirationTime;
707
- }
708
- } else if (
700
alternate !== null &&
701
(alternate.expirationTime === NoWork ||
702
alternate.expirationTime > renderExpirationTime)
703
) {
704
alternate.expirationTime = renderExpirationTime;
714
- } else {
715
- // Neither alternate was updated, which means the rest of the
716
- // ancestor path already has sufficient priority.
717
- break;
705
}
719
- node = node.return;
706
+ } else if (
707
+ alternate !== null &&
708
+ (alternate.expirationTime === NoWork ||
709
+ alternate.expirationTime > renderExpirationTime)
710
+ ) {
711
+ alternate.expirationTime = renderExpirationTime;
712
+ } else {
713
+ // Neither alternate was updated, which means the rest of the
714
+ // ancestor path already has sufficient priority.
715
+ break;
716
}
721
- // Don't scan deeper than a matching consumer. When we render the
722
- // consumer, we'll continue scanning from that point. This way the
723
- // scanning work is time-sliced.
724
- nextFiber = null;
725
- } else {
726
- // Traverse down.
727
- nextFiber = fiber.child;
717
+ node = node.return;
718
}
729
- break;
730
- case ContextProvider:
731
- // Don't scan deeper if this is a matching provider
732
- nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
733
- break;
734
- default:
719
+ // Don't scan deeper than a matching consumer. When we render the
720
+ // consumer, we'll continue scanning from that point. This way the
721
+ // scanning work is time-sliced.
722
+ nextFiber = null;
723
+ } else {
724
// Traverse down.
725
nextFiber = fiber.child;
726
+ }
727
+ break;
728
+ case ContextProvider:
729
+ // Don't scan deeper if this is a matching provider
730
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
731
+ break;
732
+ default:
733
+ // Traverse down.
734
+ nextFiber = fiber.child;
735
+ break;
736
+ }
737
+ if (nextFiber !== null) {
738
+ // Set the return pointer of the child to the work-in-progress fiber.
739
+ nextFiber.return = fiber;
740
+ } else {
741
+ // No child. Traverse to next sibling.
742
+ nextFiber = fiber;
743
+ while (nextFiber !== null) {
744
+ if (nextFiber === workInProgress) {
745
+ // We're back to the root of this subtree. Exit.
746
+ nextFiber = null;
747
+ break;
748
+ }
749
+ let sibling = nextFiber.sibling;
750
+ if (sibling !== null) {
751
+ nextFiber = sibling;
752
break;
738
- }
739
- if (nextFiber !== null) {
740
- // Set the return pointer of the child to the work-in-progress fiber.
741
- nextFiber.return = fiber;
742
- } else {
743
- // No child. Traverse to next sibling.
744
- nextFiber = fiber;
745
- while (nextFiber !== null) {
746
- if (nextFiber === workInProgress) {
747
- // We're back to the root of this subtree. Exit.
748
- nextFiber = null;
749
- break;
750
- }
751
- let sibling = nextFiber.sibling;
752
- if (sibling !== null) {
753
- nextFiber = sibling;
754
- break;
755
- }
756
- // No more siblings. Traverse up.
757
- nextFiber = nextFiber.return;
753
}
754
+ // No more siblings. Traverse up.
755
+ nextFiber = nextFiber.return;
756
}
760
- fiber = nextFiber;
757
}
758
+ fiber = nextFiber;
759
}
760
}
761
@@ -767,79 +764,75 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
764
workInProgress,
765
renderExpirationTime,
766
) {
770
- if (enableNewContextAPI) {
771
- const providerType: ReactProviderType<any> = workInProgress.type;
772
- const context: ReactContext<any> = providerType.context;
773
-
774
- const newProps = workInProgress.pendingProps;
775
- const oldProps = workInProgress.memoizedProps;
776
-
777
- if (hasLegacyContextChanged()) {
778
- // Normally we can bail out on props equality but if context has changed
779
- // we don't do the bailout and we have to reuse existing props instead.
780
- } else if (oldProps === newProps) {
781
- workInProgress.stateNode = 0;
782
- pushProvider(workInProgress);
783
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
784
- }
785
- workInProgress.memoizedProps = newProps;
767
+ const providerType: ReactProviderType<any> = workInProgress.type;
768
+ const context: ReactContext<any> = providerType.context;
769
787
- const newValue = newProps.value;
770
+ const newProps = workInProgress.pendingProps;
771
+ const oldProps = workInProgress.memoizedProps;
772
789
- let changedBits: number;
790
- if (oldProps === null) {
791
- // Initial render
792
- changedBits = MAX_SIGNED_31_BIT_INT;
773
+ if (hasLegacyContextChanged()) {
774
+ // Normally we can bail out on props equality but if context has changed
775
+ // we don't do the bailout and we have to reuse existing props instead.
776
+ } else if (oldProps === newProps) {
777
+ workInProgress.stateNode = 0;
778
+ pushProvider(workInProgress);
779
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
780
+ }
781
+ workInProgress.memoizedProps = newProps;
782
+
783
+ const newValue = newProps.value;
784
+
785
+ let changedBits: number;
786
+ if (oldProps === null) {
787
+ // Initial render
788
+ changedBits = MAX_SIGNED_31_BIT_INT;
789
+ } else {
790
+ const oldValue = oldProps.value;
791
+ // Use Object.is to compare the new context value to the old value.
792
+ // Inlined Object.is polyfill.
793
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
794
+ if (
795
+ (oldValue === newValue &&
796
+ (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
797
+ (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
798
+ ) {
799
+ // No change.
800
+ changedBits = 0;
801
} else {
794
- const oldValue = oldProps.value;
795
- // Use Object.is to compare the new context value to the old value.
796
- // Inlined Object.is polyfill.
797
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
798
- if (
799
- (oldValue === newValue &&
800
- (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
801
- (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
802
- ) {
803
- // No change.
804
- changedBits = 0;
805
- } else {
806
- changedBits =
807
- context.calculateChangedBits !== null
808
- ? context.calculateChangedBits(oldValue, newValue)
809
- : MAX_SIGNED_31_BIT_INT;
810
- if (__DEV__) {
811
- warning(
812
- (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
813
- 'calculateChangedBits: Expected the return value to be a ' +
814
- '31-bit integer. Instead received: %s',
815
- changedBits,
816
- );
817
- }
818
- changedBits |= 0;
819
-
820
- if (changedBits !== 0) {
821
- propagateContextChange(
822
- workInProgress,
823
- context,
824
- changedBits,
825
- renderExpirationTime,
826
- );
827
- }
802
+ changedBits =
803
+ typeof context.calculateChangedBits === 'function'
804
+ ? context.calculateChangedBits(oldValue, newValue)
805
+ : MAX_SIGNED_31_BIT_INT;
806
+ if (__DEV__) {
807
+ warning(
808
+ (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
809
+ 'calculateChangedBits: Expected the return value to be a ' +
810
+ '31-bit integer. Instead received: %s',
811
+ changedBits,
812
+ );
813
+ }
814
+ changedBits |= 0;
815
+
816
+ if (changedBits !== 0) {
817
+ propagateContextChange(
818
+ workInProgress,
819
+ context,
820
+ changedBits,
821
+ renderExpirationTime,
822
+ );
823
}
824
}
825
+ }
826
831
- workInProgress.stateNode = changedBits;
832
- pushProvider(workInProgress);
827
+ workInProgress.stateNode = changedBits;
828
+ pushProvider(workInProgress);
829
834
- if (oldProps !== null && oldProps.children === newProps.children) {
835
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
836
- }
837
- const newChildren = newProps.children;
838
- reconcileChildren(current, workInProgress, newChildren);
839
- return workInProgress.child;
840
- } else {
841
- return null;
830
+ if (oldProps !== null && oldProps.children === newProps.children) {
831
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
832
}
833
+ const newChildren = newProps.children;
834
+ reconcileChildren(current, workInProgress, newChildren);
835
+ return workInProgress.child;
836
}
837
838
function updateContextConsumer(
@@ -847,38 +840,35 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
840
workInProgress,
841
renderExpirationTime,
842
) {
850
- if (enableNewContextAPI) {
851
- const context: ReactContext<any> = workInProgress.type;
852
- const newProps = workInProgress.pendingProps;
843
+ const context: ReactContext<any> = workInProgress.type;
844
+ const newProps = workInProgress.pendingProps;
845
854
- const newValue = context.currentValue;
855
- const changedBits = context.changedBits;
846
+ const newValue = context.currentValue;
847
+ const changedBits = context.changedBits;
848
857
- if (changedBits !== 0) {
858
- // Context change propagation stops at matching consumers, for time-
859
- // slicing. Continue the propagation here.
860
- propagateContextChange(
861
- workInProgress,
862
- context,
863
- changedBits,
864
- renderExpirationTime,
865
- );
866
- }
867
-
868
- // Store the observedBits on the fiber's stateNode for quick access.
869
- let observedBits = newProps.observedBits;
870
- if (observedBits === undefined || observedBits === null) {
871
- // Subscribe to all changes by default
872
- observedBits = MAX_SIGNED_31_BIT_INT;
873
- }
874
- workInProgress.stateNode = observedBits;
849
+ if (changedBits !== 0) {
850
+ // Context change propagation stops at matching consumers, for time-
851
+ // slicing. Continue the propagation here.
852
+ propagateContextChange(
853
+ workInProgress,
854
+ context,
855
+ changedBits,
856
+ renderExpirationTime,
857
+ );
858
+ }
859
876
- const newChildren = newProps.render(newValue);
877
- reconcileChildren(current, workInProgress, newChildren);
878
- return workInProgress.child;
879
- } else {
880
- return null;
860
+ // Store the observedBits on the fiber's stateNode for quick access.
861
+ let observedBits = newProps.observedBits;
862
+ if (observedBits === undefined || observedBits === null) {
863
+ // Subscribe to all changes by default
864
+ observedBits = MAX_SIGNED_31_BIT_INT;
865
}
866
+ workInProgress.stateNode = observedBits;
867
+
868
+ const render = newProps.children;
869
+ const newChildren = render(newValue);
870
+ reconcileChildren(current, workInProgress, newChildren);
871
+ return workInProgress.child;
872
}
873
874
/*
packages/react-reconciler/src/ReactFiberNewContext.js
+1
@@ -67,4 +67,5 @@ export function resetProviderStack(): void {
67
context._currentRenderer = null;
68
}
69
}
70
+ index = -1;
71
}
packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
+134
-96
@@ -10,7 +10,6 @@
10
'use strict';
11
12
let ReactFeatureFlags = require('shared/ReactFeatureFlags');
13
-ReactFeatureFlags.enableNewContextAPI = true;
13
14
let React = require('react');
15
let ReactNoop;
@@ -21,7 +20,6 @@ describe('ReactNewContext', () => {
20
jest.resetModules();
21
ReactFeatureFlags = require('shared/ReactFeatureFlags');
22
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
24
- ReactFeatureFlags.enableNewContextAPI = true;
23
React = require('react');
24
ReactNoop = require('react-noop-renderer');
25
gen = require('random-seed');
@@ -37,29 +35,27 @@ describe('ReactNewContext', () => {
35
}
36
37
it('simple mount and update', () => {
40
- const Context = React.unstable_createContext(1);
41
-
42
- function Provider(props) {
43
- return Context.provide(props.value, props.children);
44
- }
38
+ const Context = React.createContext(1);
39
40
function Consumer(props) {
47
- return Context.consume(value => {
48
- return <span prop={'Result: ' + value} />;
49
- });
41
+ return (
42
+ <Context.Consumer>
43
+ {value => <span prop={'Result: ' + value} />}
44
+ </Context.Consumer>
45
+ );
46
}
47
48
const Indirection = React.Fragment;
49
50
function App(props) {
51
return (
56
- <Provider value={props.value}>
52
+ <Context.Provider value={props.value}>
53
<Indirection>
54
<Indirection>
55
<Consumer />
56
</Indirection>
57
</Indirection>
62
- </Provider>
58
+ </Context.Provider>
59
);
60
}
61
@@ -74,19 +70,27 @@ describe('ReactNewContext', () => {
70
});
71
72
it('propagates through shouldComponentUpdate false', () => {
77
- const Context = React.unstable_createContext(1);
73
+ const Context = React.createContext(1);
74
75
function Provider(props) {
76
ReactNoop.yield('Provider');
81
- return Context.provide(props.value, props.children);
77
+ return (
78
+ <Context.Provider value={props.value}>
79
+ {props.children}
80
+ </Context.Provider>
81
+ );
82
}
83
84
function Consumer(props) {
85
ReactNoop.yield('Consumer');
86
- return Context.consume(value => {
87
- ReactNoop.yield('Consumer render prop');
88
- return <span prop={'Result: ' + value} />;
89
- });
86
+ return (
87
+ <Context.Consumer>
88
+ {value => {
89
+ ReactNoop.yield('Consumer render prop');
90
+ return <span prop={'Result: ' + value} />;
91
+ }}
92
+ </Context.Consumer>
93
+ );
94
}
95
96
class Indirection extends React.Component {
@@ -134,19 +138,27 @@ describe('ReactNewContext', () => {
138
});
139
140
it('consumers bail out if context value is the same', () => {
137
- const Context = React.unstable_createContext(1);
141
+ const Context = React.createContext(1);
142
143
function Provider(props) {
144
ReactNoop.yield('Provider');
141
- return Context.provide(props.value, props.children);
145
+ return (
146
+ <Context.Provider value={props.value}>
147
+ {props.children}
148
+ </Context.Provider>
149
+ );
150
}
151
152
function Consumer(props) {
153
ReactNoop.yield('Consumer');
146
- return Context.consume(value => {
147
- ReactNoop.yield('Consumer render prop');
148
- return <span prop={'Result: ' + value} />;
149
- });
154
+ return (
155
+ <Context.Consumer>
156
+ {value => {
157
+ ReactNoop.yield('Consumer render prop');
158
+ return <span prop={'Result: ' + value} />;
159
+ }}
160
+ </Context.Consumer>
161
+ );
162
}
163
164
class Indirection extends React.Component {
@@ -194,19 +206,27 @@ describe('ReactNewContext', () => {
206
});
207
208
it('nested providers', () => {
197
- const Context = React.unstable_createContext(1);
209
+ const Context = React.createContext(1);
210
211
function Provider(props) {
200
- return Context.consume(contextValue =>
201
- // Multiply previous context value by 2, unless prop overrides
202
- Context.provide(props.value || contextValue * 2, props.children),
212
+ return (
213
+ <Context.Consumer>
214
+ {contextValue => (
215
+ // Multiply previous context value by 2, unless prop overrides
216
+ <Context.Provider value={props.value || contextValue * 2}>
217
+ {props.children}
218
+ </Context.Provider>
219
+ )}
220
+ </Context.Consumer>
221
);
222
}
223
224
function Consumer(props) {
207
- return Context.consume(value => {
208
- return <span prop={'Result: ' + value} />;
209
- });
225
+ return (
226
+ <Context.Consumer>
227
+ {value => <span prop={'Result: ' + value} />}
228
+ </Context.Consumer>
229
+ );
230
}
231
232
class Indirection extends React.Component {
@@ -247,19 +267,27 @@ describe('ReactNewContext', () => {
267
});
268
269
it('multiple consumers in different branches', () => {
250
- const Context = React.unstable_createContext(1);
270
+ const Context = React.createContext(1);
271
272
function Provider(props) {
253
- return Context.consume(contextValue =>
254
- // Multiply previous context value by 2, unless prop overrides
255
- Context.provide(props.value || contextValue * 2, props.children),
273
+ return (
274
+ <Context.Consumer>
275
+ {contextValue => (
276
+ // Multiply previous context value by 2, unless prop overrides
277
+ <Context.Provider value={props.value || contextValue * 2}>
278
+ {props.children}
279
+ </Context.Provider>
280
+ )}
281
+ </Context.Consumer>
282
);
283
}
284
285
function Consumer(props) {
260
- return Context.consume(value => {
261
- return <span prop={'Result: ' + value} />;
262
- });
286
+ return (
287
+ <Context.Consumer>
288
+ {value => <span prop={'Result: ' + value} />}
289
+ </Context.Consumer>
290
+ );
291
}
292
293
class Indirection extends React.Component {
@@ -313,19 +341,27 @@ describe('ReactNewContext', () => {
341
});
342
343
it('compares context values with Object.is semantics', () => {
316
- const Context = React.unstable_createContext(1);
344
+ const Context = React.createContext(1);
345
346
function Provider(props) {
347
ReactNoop.yield('Provider');
320
- return Context.provide(props.value, props.children);
348
+ return (
349
+ <Context.Provider value={props.value}>
350
+ {props.children}
351
+ </Context.Provider>
352
+ );
353
}
354
355
function Consumer(props) {
356
ReactNoop.yield('Consumer');
325
- return Context.consume(value => {
326
- ReactNoop.yield('Consumer render prop');
327
- return <span prop={'Result: ' + value} />;
328
- });
357
+ return (
358
+ <Context.Consumer>
359
+ {value => {
360
+ ReactNoop.yield('Consumer render prop');
361
+ return <span prop={'Result: ' + value} />;
362
+ }}
363
+ </Context.Consumer>
364
+ );
365
}
366
367
class Indirection extends React.Component {
@@ -374,16 +410,14 @@ describe('ReactNewContext', () => {
410
});
411
412
it('context unwinds when interrupted', () => {
377
- const Context = React.unstable_createContext('Default');
378
-
379
- function Provider(props) {
380
- return Context.provide(props.value, props.children);
381
- }
413
+ const Context = React.createContext('Default');
414
415
function Consumer(props) {
384
- return Context.consume(value => {
385
- return <span prop={'Result: ' + value} />;
386
- });
416
+ return (
417
+ <Context.Consumer>
418
+ {value => <span prop={'Result: ' + value} />}
419
+ </Context.Consumer>
420
+ );
421
}
422
423
function BadRender() {
@@ -406,14 +440,14 @@ describe('ReactNewContext', () => {
440
function App(props) {
441
return (
442
<React.Fragment>
409
- <Provider value="Does not unwind">
443
+ <Context.Provider value="Does not unwind">
444
<ErrorBoundary>
411
- <Provider value="Unwinds after BadRender throws">
445
+ <Context.Provider value="Unwinds after BadRender throws">
446
<BadRender />
413
- </Provider>
447
+ </Context.Provider>
448
</ErrorBoundary>
449
<Consumer />
416
- </Provider>
450
+ </Context.Provider>
451
</React.Fragment>
452
);
453
}
@@ -421,13 +455,13 @@ describe('ReactNewContext', () => {
455
ReactNoop.render(<App value="A" />);
456
ReactNoop.flush();
457
expect(ReactNoop.getChildren()).toEqual([
424
- // The second provider should use the default value. This proves the
458
+ // The second provider should use the default value.
459
span('Result: Does not unwind'),
460
]);
461
});
462
463
it('can skip consumers with bitmask', () => {
430
- const Context = React.unstable_createContext({foo: 0, bar: 0}, (a, b) => {
464
+ const Context = React.createContext({foo: 0, bar: 0}, (a, b) => {
465
let result = 0;
466
if (a.foo !== b.foo) {
467
result |= 0b01;
@@ -439,21 +473,33 @@ describe('ReactNewContext', () => {
473
});
474
475
function Provider(props) {
442
- return Context.provide({foo: props.foo, bar: props.bar}, props.children);
476
+ return (
477
+ <Context.Provider value={{foo: props.foo, bar: props.bar}}>
478
+ {props.children}
479
+ </Context.Provider>
480
+ );
481
}
482
483
function Foo() {
446
- return Context.consume(value => {
447
- ReactNoop.yield('Foo');
448
- return <span prop={'Foo: ' + value.foo} />;
449
- }, 0b01);
484
+ return (
485
+ <Context.Consumer observedBits={0b01}>
486
+ {value => {
487
+ ReactNoop.yield('Foo');
488
+ return <span prop={'Foo: ' + value.foo} />;
489
+ }}
490
+ </Context.Consumer>
491
+ );
492
}
493
494
function Bar() {
453
- return Context.consume(value => {
454
- ReactNoop.yield('Bar');
455
- return <span prop={'Bar: ' + value.bar} />;
456
- }, 0b10);
495
+ return (
496
+ <Context.Consumer observedBits={0b10}>
497
+ {value => {
498
+ ReactNoop.yield('Bar');
499
+ return <span prop={'Bar: ' + value.bar} />;
500
+ }}
501
+ </Context.Consumer>
502
+ );
503
}
504
505
class Indirection extends React.Component {
@@ -503,20 +549,16 @@ describe('ReactNewContext', () => {
549
it('warns if calculateChangedBits returns larger than a 31-bit integer', () => {
550
spyOnDev(console, 'error');
551
506
- const Context = React.unstable_createContext(
552
+ const Context = React.createContext(
553
0,
554
(a, b) => Math.pow(2, 32) - 1, // Return 32 bit int
555
);
556
511
- function Provider(props) {
512
- return Context.provide(props.value, props.children);
513
- }
514
-
515
- ReactNoop.render(<Provider value={1} />);
557
+ ReactNoop.render(<Context.Provider value={1} />);
558
ReactNoop.flush();
559
560
// Update
519
- ReactNoop.render(<Provider value={2} />);
561
+ ReactNoop.render(<Context.Provider value={2} />);
562
ReactNoop.flush();
563
564
if (__DEV__) {
@@ -530,22 +572,19 @@ describe('ReactNewContext', () => {
572
573
it('warns if multiple renderers concurrently render the same context', () => {
574
spyOnDev(console, 'error');
533
- const Context = React.unstable_createContext(0);
575
+ const Context = React.createContext(0);
576
577
function Foo(props) {
578
ReactNoop.yield('Foo');
579
return null;
580
}
539
- function Provider(props) {
540
- return Context.provide(props.value, props.children);
541
- }
581
582
function App(props) {
583
return (
545
- <Provider value={props.value}>
584
+ <Context.Provider value={props.value}>
585
<Foo />
586
<Foo />
548
- </Provider>
587
+ </Context.Provider>
588
);
589
}
590
@@ -556,7 +595,6 @@ describe('ReactNewContext', () => {
595
// Get a new copy of ReactNoop
596
jest.resetModules();
597
ReactFeatureFlags = require('shared/ReactFeatureFlags');
559
- ReactFeatureFlags.enableNewContextAPI = true;
598
React = require('react');
599
ReactNoop = require('react-noop-renderer');
600
@@ -641,7 +679,7 @@ describe('ReactNewContext', () => {
679
function ContextSimulator(maxDepth) {
680
const contexts = new Map(
681
contextKeys.map(key => {
644
- const Context = React.unstable_createContext(0);
682
+ const Context = React.createContext(0);
683
Context.displayName = 'Context' + key;
684
return [key, Context];
685
}),
@@ -661,19 +699,19 @@ describe('ReactNewContext', () => {
699
this.props.rand.intBetween(0, contextKeys.length - 1)
700
];
701
const Context = contexts.get(randomKey);
664
- return Context.consume(
665
- value => (
666
- <Fragment>
667
- <span prop={`${randomKey}:${value}`} />
668
- <ConsumerTree
669
- rand={this.props.rand}
670
- depth={this.props.depth + 1}
671
- maxDepth={this.props.maxDepth}
672
- />
673
- </Fragment>
674
- ),
675
- null,
676
- i,
702
+ return (
703
+ <Context.Consumer key={i}>
704
+ {value => (
705
+ <Fragment>
706
+ <span prop={`${randomKey}:${value}`} />
707
+ <ConsumerTree
708
+ rand={this.props.rand}
709
+ depth={this.props.depth + 1}
710
+ maxDepth={this.props.maxDepth}
711
+ />
712
+ </Fragment>
713
+ )}
714
+ </Context.Consumer>
715
);
716
});
717
return consumers;
@@ -684,7 +722,7 @@ describe('ReactNewContext', () => {
722
return contextKeys.reduceRight((children, key) => {
723
const Context = contexts.get(key);
724
const value = props.values[key];
687
- return Context.provide(value, children);
725
+ return <Context.Provider value={value}>{children}</Context.Provider>;
726
}, <ConsumerTree rand={props.rand} depth={0} maxDepth={props.maxDepth} />);
727
}
728
packages/react/src/React.js
+2
-5
@@ -29,7 +29,6 @@ import {
29
cloneElementWithValidation,
30
} from './ReactElementValidator';
31
import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
32
-import {enableNewContextAPI} from 'shared/ReactFeatureFlags';
32
33
const React = {
34
Children: {
@@ -43,6 +42,8 @@ const React = {
42
Component,
43
PureComponent,
44
45
+ createContext,
46
+
47
Fragment: REACT_FRAGMENT_TYPE,
48
StrictMode: REACT_STRICT_MODE_TYPE,
49
unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
@@ -61,10 +62,6 @@ const React = {
62
},
63
};
64
64
-if (enableNewContextAPI) {
65
- React.unstable_createContext = createContext;
66
-}
67
-
65
if (__DEV__) {
66
Object.assign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
67
// These should not be included in production.
packages/react/src/ReactElementValidator.js
+7
-1
@@ -20,6 +20,8 @@ import {
20
REACT_FRAGMENT_TYPE,
21
REACT_STRICT_MODE_TYPE,
22
REACT_ASYNC_MODE_TYPE,
23
+ REACT_PROVIDER_TYPE,
24
+ REACT_CONTEXT_TYPE,
25
} from 'shared/ReactSymbols';
26
import checkPropTypes from 'prop-types/checkPropTypes';
27
import warning from 'fbjs/lib/warning';
@@ -289,7 +291,11 @@ export function createElementWithValidation(type, props, children) {
291
// Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
292
type === REACT_FRAGMENT_TYPE ||
293
type === REACT_ASYNC_MODE_TYPE ||
292
- type === REACT_STRICT_MODE_TYPE;
294
+ type === REACT_STRICT_MODE_TYPE ||
295
+ (typeof type === 'object' &&
296
+ type !== null &&
297
+ (type.$$typeof === REACT_PROVIDER_TYPE ||
298
+ type.$$typeof === REACT_CONTEXT_TYPE));
299
300
// We warn in this case but don't throw. We expect the element creation to
301
// succeed and there will likely be errors in render.
packages/shared/ReactContext.js
+7
-42
@@ -7,18 +7,9 @@
7
* @flow
8
*/
9
10
-import {
11
- REACT_PROVIDER_TYPE,
12
- REACT_CONTEXT_TYPE,
13
- REACT_ELEMENT_TYPE,
14
-} from 'shared/ReactSymbols';
10
+import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
11
16
-import type {
17
- ReactContext,
18
- ReactConsumer,
19
- ReactProvider,
20
- ReactNodeList,
21
-} from 'shared/ReactTypes';
12
+import type {ReactContext} from 'shared/ReactTypes';
13
14
import warning from 'fbjs/lib/warning';
15
@@ -26,8 +17,6 @@ export function createContext<T>(
17
defaultValue: T,
18
calculateChangedBits: ?(a: T, b: T) => number,
19
): ReactContext<T> {
29
- let providerType;
30
-
20
if (calculateChangedBits === undefined) {
21
calculateChangedBits = null;
22
} else {
@@ -44,44 +33,20 @@ export function createContext<T>(
33
34
const context: ReactContext<T> = {
35
$$typeof: REACT_CONTEXT_TYPE,
47
- provide(value: T, children: ReactNodeList, key?: string): ReactProvider<T> {
48
- return {
49
- $$typeof: REACT_ELEMENT_TYPE,
50
- type: providerType,
51
- key: key === null || key === undefined ? null : '' + key,
52
- ref: null,
53
- props: {
54
- value,
55
- children,
56
- },
57
- };
58
- },
59
- consume(
60
- render: (value: T) => ReactNodeList,
61
- observedBits?: number,
62
- key?: string,
63
- ): ReactConsumer<T> {
64
- return {
65
- $$typeof: REACT_ELEMENT_TYPE,
66
- type: context,
67
- key: key === null || key === undefined ? null : '' + key,
68
- ref: null,
69
- props: {
70
- observedBits,
71
- render,
72
- },
73
- };
74
- },
36
calculateChangedBits,
37
defaultValue,
38
currentValue: defaultValue,
39
changedBits: 0,
40
+ // These are circular
41
+ Provider: (null: any),
42
+ Consumer: (null: any),
43
};
44
81
- providerType = {
45
+ context.Provider = {
46
$$typeof: REACT_PROVIDER_TYPE,
47
context,
48
};
49
+ context.Consumer = context;
50
51
if (__DEV__) {
52
context._currentRenderer = null;
packages/shared/ReactFeatureFlags.js
-3
@@ -19,9 +19,6 @@ export const enableMutatingReconciler = true;
19
export const enableNoopReconciler = false;
20
// Experimental persistent mode (Fabric):
21
export const enablePersistentReconciler = false;
22
-// Support for new context API
23
-export const enableNewContextAPI = false;
24
-
22
// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
23
export const debugRenderPhaseSideEffects = false;
24
packages/shared/ReactTypes.js
+3
-7
@@ -71,19 +71,15 @@ export type ReactConsumer<T> = {
71
key: null | string,
72
ref: null,
73
props: {
74
- render: (value: T) => ReactNodeList,
74
+ children: (value: T) => ReactNodeList,
75
bits?: number,
76
},
77
};
78
79
export type ReactContext<T> = {
80
$$typeof: Symbol | number,
81
- provide(value: T, children: ReactNodeList, key?: string): ReactProvider<T>,
82
- consume(
83
- render: (value: T) => ReactNodeList,
84
- observedBits?: number,
85
- key?: string,
86
- ): ReactConsumer<T>,
81
+ Consumer: ReactContext<T>,
82
+ Provider: ReactProviderType<T>,
83
calculateChangedBits: ((a: T, b: T) => number) | null,
84
defaultValue: T,
85
currentValue: T,
packages/shared/forks/ReactFeatureFlags.native-fabric.js
-1
@@ -17,7 +17,6 @@ export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableCreateRoot = false;
18
export const enableUserTimingAPI = __DEV__;
19
export const warnAboutDeprecatedLifecycles = false;
20
-export const enableNewContextAPI = false;
20
21
// React Fabric uses persistent reconciler.
22
export const enableMutatingReconciler = false;
packages/shared/forks/ReactFeatureFlags.native.js
-1
@@ -25,7 +25,6 @@ export const enableUserTimingAPI = __DEV__;
25
export const enableMutatingReconciler = true;
26
export const enableNoopReconciler = false;
27
export const enablePersistentReconciler = false;
28
-export const enableNewContextAPI = false;
28
29
// Only used in www builds.
30
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -19,7 +19,6 @@ export const {
19
20
// The rest of the flags are static for better dead code elimination.
21
export const enableCreateRoot = true;
22
-export const enableNewContextAPI = true;
22
23
// The www bundles only use the mutating reconciler.
24
export const enableMutatingReconciler = true;