@samitouri / QOS-React / commits / e6e393b9c5

Add warning in server renderer if class doesn't extend React.Component (#11993)

* Add warning in server renderer if class doesn't extend React.Component In dev mode, while server rendering, a warning will be thrown if there is a class that doesn't extend React.Component. * Use `.toWarnDev` matcher and deduplicate warnings * Deduplicate client-side warning if class doesn't extend React.Component * Default componentName to Unknown if null

Neil Kistner committed Jan 9, 2018 at 10:24 UTC e6e393b9c5221bfb1a5ddcc7221c42e96ab3baca
4 files changed +62 -8
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+5
@@ -338,6 +338,11 @@ describe('ReactCompositeComponent', () => {
338 'Change ClassWithRenderNotExtended to extend React.Component instead.',
339 );
340 }).toThrow(TypeError);
341 +
342 + // Test deduplication
343 + expect(() => {
344 + ReactDOM.render(<ClassWithRenderNotExtended />, container);
345 + }).toThrow(TypeError);
346 });
347
348 it('should warn about `setState` in render', () => {
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+23
@@ -554,4 +554,27 @@ describe('ReactDOMServer', () => {
554 'The experimental Call and Return types are not currently supported by the server renderer.',
555 );
556 });
557 +
558 + it('should warn when server rendering a class with a render method that does not extend React.Component', () => {
559 + class ClassWithRenderNotExtended {
560 + render() {
561 + return <div />;
562 + }
563 + }
564 +
565 + expect(() => {
566 + expect(() =>
567 + ReactDOMServer.renderToString(<ClassWithRenderNotExtended />),
568 + ).toWarnDev(
569 + 'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
570 + "but doesn't extend React.Component. This is likely to cause errors. " +
571 + 'Change ClassWithRenderNotExtended to extend React.Component instead.',
572 + );
573 + }).toThrow(TypeError);
574 +
575 + // Test deduplication
576 + expect(() => {
577 + ReactDOMServer.renderToString(<ClassWithRenderNotExtended />);
578 + }).toThrow(TypeError);
579 + });
580 });
packages/react-dom/src/server/ReactPartialRenderer.js
+20
@@ -122,6 +122,7 @@ let didWarnDefaultSelectValue = false;
122 let didWarnDefaultTextareaValue = false;
123 let didWarnInvalidOptionChildren = false;
124 const didWarnAboutNoopUpdateForComponent = {};
125 +const didWarnAboutBadClass = {};
126 const valuePropNames = ['value', 'defaultValue'];
127 const newlineEatingTags = {
128 listing: true,
@@ -421,6 +422,25 @@ function resolve(
422 if (shouldConstruct(Component)) {
423 inst = new Component(element.props, publicContext, updater);
424 } else {
425 + if (__DEV__) {
426 + if (
427 + Component.prototype &&
428 + typeof Component.prototype.render === 'function'
429 + ) {
430 + const componentName = getComponentName(Component) || 'Unknown';
431 +
432 + if (!didWarnAboutBadClass[componentName]) {
433 + warning(
434 + false,
435 + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
436 + 'This is likely to cause errors. Change %s to extend React.Component instead.',
437 + componentName,
438 + componentName,
439 + );
440 + didWarnAboutBadClass[componentName] = true;
441 + }
442 + }
443 + }
444 inst = Component(element.props, publicContext, updater);
445 if (inst == null || inst.render == null) {
446 child = inst;
packages/react-reconciler/src/ReactFiberBeginWork.js
+14 -8
@@ -61,9 +61,11 @@ import {NoWork, Never} from './ReactFiberExpirationTime';
61 import {AsyncUpdates} from './ReactTypeOfInternalContext';
62
63 let warnedAboutStatelessRefs;
64 +let didWarnAboutBadClass;
65
66 if (__DEV__) {
67 warnedAboutStatelessRefs = {};
68 + didWarnAboutBadClass = {};
69 }
70
71 export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
@@ -455,14 +457,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
457
458 if (__DEV__) {
459 if (fn.prototype && typeof fn.prototype.render === 'function') {
458 - const componentName = getComponentName(workInProgress);
459 - warning(
460 - false,
461 - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
462 - 'This is likely to cause errors. Change %s to extend React.Component instead.',
463 - componentName,
464 - componentName,
465 - );
460 + const componentName = getComponentName(workInProgress) || 'Unknown';
461 +
462 + if (!didWarnAboutBadClass[componentName]) {
463 + warning(
464 + false,
465 + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
466 + 'This is likely to cause errors. Change %s to extend React.Component instead.',
467 + componentName,
468 + componentName,
469 + );
470 + didWarnAboutBadClass[componentName] = true;
471 + }
472 }
473 ReactCurrentOwner.current = workInProgress;
474 value = fn(props, context);