@samitouri / QOS-React / commits / acd65db5bc

Deprecate module pattern (factory) components (#15145)

Sebastian Markbåge committed Mar 19, 2019 at 12:55 UTC acd65db5bc2cd1ab19be66318f3e931054796739
14 files changed +151 -41
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+10 -1
@@ -977,7 +977,16 @@ describe('ReactComponentLifeCycle', () => {
977 };
978
979 const div = document.createElement('div');
980 - ReactDOM.render(<Parent ref={c => c && log.push('ref')} />, div);
980 + expect(() =>
981 + ReactDOM.render(<Parent ref={c => c && log.push('ref')} />, div),
982 + ).toWarnDev(
983 + 'Warning: The <Parent /> component appears to be a function component that returns a class instance. ' +
984 + 'Change Parent to a class that extends React.Component instead. ' +
985 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
986 + '`Parent.prototype = React.Component.prototype`. ' +
987 + "Don't use an arrow function since it cannot be called with `new` by React.",
988 + {withoutStack: true},
989 + );
990 ReactDOM.render(<Parent ref={c => c && log.push('ref')} />, div);
991
992 expect(log).toEqual([
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+8 -1
@@ -118,7 +118,14 @@ describe('ReactCompositeComponent', () => {
118 }
119
120 const el = document.createElement('div');
121 - ReactDOM.render(<Child test="test" />, el);
121 + expect(() => ReactDOM.render(<Child test="test" />, el)).toWarnDev(
122 + 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
123 + 'Change Child to a class that extends React.Component instead. ' +
124 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
125 + '`Child.prototype = React.Component.prototype`. ' +
126 + "Don't use an arrow function since it cannot be called with `new` by React.",
127 + {withoutStack: true},
128 + );
129
130 expect(el.textContent).toBe('test');
131 });
packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
+8 -1
@@ -474,7 +474,14 @@ describe('ReactCompositeComponent-state', () => {
474 }
475
476 const el = document.createElement('div');
477 - ReactDOM.render(<Child />, el);
477 + expect(() => ReactDOM.render(<Child />, el)).toWarnDev(
478 + 'Warning: The <Child /> component appears to be a function component that returns a class instance. ' +
479 + 'Change Child to a class that extends React.Component instead. ' +
480 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
481 + '`Child.prototype = React.Component.prototype`. ' +
482 + "Don't use an arrow function since it cannot be called with `new` by React.",
483 + {withoutStack: true},
484 + );
485
486 expect(el.textContent).toBe('count:123');
487 });
packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+1 -1
@@ -644,7 +644,7 @@ describe('ReactDOMServerIntegration', () => {
644 },
645 };
646 };
647 - checkFooDiv(await render(<FactoryComponent />));
647 + checkFooDiv(await render(<FactoryComponent />, 1));
648 });
649 });
650
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+16 -5
@@ -768,12 +768,23 @@ describe('ReactErrorBoundaries', () => {
768 };
769
770 const container = document.createElement('div');
771 - ReactDOM.render(
772 - <ErrorBoundary>
773 - <BrokenComponentWillMountWithContext />
774 - </ErrorBoundary>,
775 - container,
771 + expect(() =>
772 + ReactDOM.render(
773 + <ErrorBoundary>
774 + <BrokenComponentWillMountWithContext />
775 + </ErrorBoundary>,
776 + container,
777 + ),
778 + ).toWarnDev(
779 + 'Warning: The <BrokenComponentWillMountWithContext /> component appears to be a function component that ' +
780 + 'returns a class instance. ' +
781 + 'Change BrokenComponentWillMountWithContext to a class that extends React.Component instead. ' +
782 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
783 + '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
784 + "Don't use an arrow function since it cannot be called with `new` by React.",
785 + {withoutStack: true},
786 );
787 +
788 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
789 });
790
packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
+15 -5
@@ -742,11 +742,21 @@ describe('ReactLegacyErrorBoundaries', () => {
742 };
743
744 const container = document.createElement('div');
745 - ReactDOM.render(
746 - <ErrorBoundary>
747 - <BrokenComponentWillMountWithContext />
748 - </ErrorBoundary>,
749 - container,
745 + expect(() =>
746 + ReactDOM.render(
747 + <ErrorBoundary>
748 + <BrokenComponentWillMountWithContext />
749 + </ErrorBoundary>,
750 + container,
751 + ),
752 + ).toWarnDev(
753 + 'Warning: The <BrokenComponentWillMountWithContext /> component appears to be a function component that ' +
754 + 'returns a class instance. ' +
755 + 'Change BrokenComponentWillMountWithContext to a class that extends React.Component instead. ' +
756 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
757 + '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
758 + "Don't use an arrow function since it cannot be called with `new` by React.",
759 + {withoutStack: true},
760 );
761 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
762 });
packages/react-dom/src/__tests__/refs-test.js
+11 -1
@@ -166,7 +166,17 @@ describe('factory components', () => {
166 };
167 }
168
169 - const inst = ReactTestUtils.renderIntoDocument(<Comp />);
169 + let inst;
170 + expect(
171 + () => (inst = ReactTestUtils.renderIntoDocument(<Comp />)),
172 + ).toWarnDev(
173 + 'Warning: The <Comp /> component appears to be a function component that returns a class instance. ' +
174 + 'Change Comp to a class that extends React.Component instead. ' +
175 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
176 + '`Comp.prototype = React.Component.prototype`. ' +
177 + "Don't use an arrow function since it cannot be called with `new` by React.",
178 + {withoutStack: true},
179 + );
180 expect(inst.refs.elemRef.tagName).toBe('DIV');
181 });
182 });
packages/react-dom/src/server/ReactPartialRenderer.js
+19
@@ -179,6 +179,7 @@ let didWarnDefaultTextareaValue = false;
179 let didWarnInvalidOptionChildren = false;
180 const didWarnAboutNoopUpdateForComponent = {};
181 const didWarnAboutBadClass = {};
182 +const didWarnAboutModulePatternComponent = {};
183 const didWarnAboutDeprecatedWillMount = {};
184 const didWarnAboutUndefinedDerivedState = {};
185 const didWarnAboutUninitializedState = {};
@@ -525,6 +526,24 @@ function resolve(
526 validateRenderResult(child, Component);
527 return;
528 }
529 +
530 + if (__DEV__) {
531 + const componentName = getComponentName(Component) || 'Unknown';
532 + if (!didWarnAboutModulePatternComponent[componentName]) {
533 + warningWithoutStack(
534 + false,
535 + 'The <%s /> component appears to be a function component that returns a class instance. ' +
536 + 'Change %s to a class that extends React.Component instead. ' +
537 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
538 + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
539 + 'cannot be called with `new` by React.',
540 + componentName,
541 + componentName,
542 + componentName,
543 + );
544 + didWarnAboutModulePatternComponent[componentName] = true;
545 + }
546 + }
547 }
548
549 inst.props = element.props;
packages/react-reconciler/src/ReactFiberBeginWork.js
+20
@@ -144,6 +144,7 @@ const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
144 let didReceiveUpdate: boolean = false;
145
146 let didWarnAboutBadClass;
147 +let didWarnAboutModulePatternComponent;
148 let didWarnAboutContextTypeOnFunctionComponent;
149 let didWarnAboutGetDerivedStateOnFunctionComponent;
150 let didWarnAboutFunctionRefs;
@@ -151,6 +152,7 @@ export let didWarnAboutReassigningProps;
152
153 if (__DEV__) {
154 didWarnAboutBadClass = {};
155 + didWarnAboutModulePatternComponent = {};
156 didWarnAboutContextTypeOnFunctionComponent = {};
157 didWarnAboutGetDerivedStateOnFunctionComponent = {};
158 didWarnAboutFunctionRefs = {};
@@ -1222,6 +1224,24 @@ function mountIndeterminateComponent(
1224 typeof value.render === 'function' &&
1225 value.$$typeof === undefined
1226 ) {
1227 + if (__DEV__) {
1228 + const componentName = getComponentName(Component) || 'Unknown';
1229 + if (!didWarnAboutModulePatternComponent[componentName]) {
1230 + warningWithoutStack(
1231 + false,
1232 + 'The <%s /> component appears to be a function component that returns a class instance. ' +
1233 + 'Change %s to a class that extends React.Component instead. ' +
1234 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
1235 + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
1236 + 'cannot be called with `new` by React.',
1237 + componentName,
1238 + componentName,
1239 + componentName,
1240 + );
1241 + didWarnAboutModulePatternComponent[componentName] = true;
1242 + }
1243 + }
1244 +
1245 // Proceed under the assumption that this is a class instance
1246 workInProgress.tag = ClassComponent;
1247
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+8 -1
@@ -1327,7 +1327,14 @@ describe('ReactHooks', () => {
1327 expect(renderCount).toBe(1);
1328
1329 renderCount = 0;
1330 - renderer.update(<Factory />);
1330 + expect(() => renderer.update(<Factory />)).toWarnDev(
1331 + 'Warning: The <Factory /> component appears to be a function component that returns a class instance. ' +
1332 + 'Change Factory to a class that extends React.Component instead. ' +
1333 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
1334 + '`Factory.prototype = React.Component.prototype`. ' +
1335 + "Don't use an arrow function since it cannot be called with `new` by React.",
1336 + {withoutStack: true},
1337 + );
1338 expect(renderCount).toBe(1);
1339 renderCount = 0;
1340 renderer.update(<Factory />);
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+16 -7
@@ -137,13 +137,22 @@ describe('ReactHooksWithNoopRenderer', () => {
137 };
138 }
139 ReactNoop.render(<Counter />);
140 - expect(Scheduler).toFlushAndThrow(
141 - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
142 - ' one of the following reasons:\n' +
143 - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
144 - '2. You might be breaking the Rules of Hooks\n' +
145 - '3. You might have more than one copy of React in the same app\n' +
146 - 'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
140 + expect(() =>
141 + expect(Scheduler).toFlushAndThrow(
142 + 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen ' +
143 + 'for one of the following reasons:\n' +
144 + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
145 + '2. You might be breaking the Rules of Hooks\n' +
146 + '3. You might have more than one copy of React in the same app\n' +
147 + 'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
148 + ),
149 + ).toWarnDev(
150 + 'Warning: The <Counter /> component appears to be a function component that returns a class instance. ' +
151 + 'Change Counter to a class that extends React.Component instead. ' +
152 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
153 + '`Counter.prototype = React.Component.prototype`. ' +
154 + "Don't use an arrow function since it cannot be called with `new` by React.",
155 + {withoutStack: true},
156 );
157
158 // Confirm that a subsequent hook works properly.
packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
+9 -2
@@ -2026,8 +2026,15 @@ describe('ReactIncremental', () => {
2026
2027 ReactNoop.render(<Recurse />);
2028 expect(() => expect(Scheduler).toFlushWithoutYielding()).toWarnDev(
2029 - 'Legacy context API has been detected within a strict-mode tree: \n\n' +
2030 - 'Please update the following components: Recurse',
2029 + [
2030 + 'Warning: The <Recurse /> component appears to be a function component that returns a class instance. ' +
2031 + 'Change Recurse to a class that extends React.Component instead. ' +
2032 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
2033 + '`Recurse.prototype = React.Component.prototype`. ' +
2034 + "Don't use an arrow function since it cannot be called with `new` by React.",
2035 + 'Legacy context API has been detected within a strict-mode tree: \n\n' +
2036 + 'Please update the following components: Recurse',
2037 + ],
2038 {withoutStack: true},
2039 );
2040 expect(ops).toEqual([
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+9 -2
@@ -1596,8 +1596,15 @@ describe('ReactIncrementalErrorHandling', () => {
1596 expect(() => {
1597 expect(Scheduler).toFlushAndThrow('Oops!');
1598 }).toWarnDev(
1599 - 'Legacy context API has been detected within a strict-mode tree: \n\n' +
1600 - 'Please update the following components: Provider',
1599 + [
1600 + 'Warning: The <Provider /> component appears to be a function component that returns a class instance. ' +
1601 + 'Change Provider to a class that extends React.Component instead. ' +
1602 + "If you can't use a class try assigning the prototype on the function as a workaround. " +
1603 + '`Provider.prototype = React.Component.prototype`. ' +
1604 + "Don't use an arrow function since it cannot be called with `new` by React.",
1605 + 'Legacy context API has been detected within a strict-mode tree: \n\n' +
1606 + 'Please update the following components: Provider',
1607 + ],
1608 {withoutStack: true},
1609 );
1610 });
packages/react/src/__tests__/ReactStrictMode-test.internal.js
+1 -14
@@ -835,7 +835,6 @@ describe('ReactStrictMode', () => {
835 <div>
836 <LegacyContextConsumer />
837 <FunctionalLegacyContextConsumer />
838 - <FactoryLegacyContextConsumer />
838 </div>
839 );
840 }
@@ -845,14 +844,6 @@ describe('ReactStrictMode', () => {
844 return null;
845 }
846
848 - function FactoryLegacyContextConsumer() {
849 - return {
850 - render() {
851 - return null;
852 - },
853 - };
854 - }
855 -
847 LegacyContextProvider.childContextTypes = {
848 color: PropTypes.string,
849 };
@@ -885,10 +876,6 @@ describe('ReactStrictMode', () => {
876 color: PropTypes.string,
877 };
878
888 - FactoryLegacyContextConsumer.contextTypes = {
889 - color: PropTypes.string,
890 - };
891 -
879 let rendered;
880
881 expect(() => {
@@ -898,7 +885,7 @@ describe('ReactStrictMode', () => {
885 '\n in StrictMode (at **)' +
886 '\n in div (at **)' +
887 '\n in Root (at **)' +
901 - '\n\nPlease update the following components: FactoryLegacyContextConsumer, ' +
888 + '\n\nPlease update the following components: ' +
889 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' +
890 '\n\nLearn more about this warning here:' +
891 '\nhttps://fb.me/react-strict-mode-warnings',