@samitouri / QOS-React / commits / 28aa084ad8

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;