From 50a0d6996112376d6d11e06103bb9acf8533fd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=E2=80=99Shannessy?= Date: Tue, 16 Dec 2014 13:57:03 -0800 Subject: [PATCH] Protect better against createElement(null/undefined) This adds a warning to React.createElement in __DEV__ about using null or undefined. This is technically valid since element creation can be considered safe and usable in multiple rendering environments. But rendering in a DOM environment with an element with null/undefined type is not safe. --- src/classic/element/ReactElement.js | 2 +- src/classic/element/ReactElementValidator.js | 44 ++++++++++++------- .../__tests__/ReactElementValidator-test.js | 22 ++++++++++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/classic/element/ReactElement.js b/src/classic/element/ReactElement.js index 013bc3eac26..e00d36c7bfa 100644 --- a/src/classic/element/ReactElement.js +++ b/src/classic/element/ReactElement.js @@ -179,7 +179,7 @@ ReactElement.createElement = function(type, config, children) { } // Resolve default props - if (type.defaultProps) { + if (type && type.defaultProps) { var defaultProps = type.defaultProps; for (propName in defaultProps) { if (typeof props[propName] === 'undefined') { diff --git a/src/classic/element/ReactElementValidator.js b/src/classic/element/ReactElementValidator.js index 687183bf654..55624f8e893 100644 --- a/src/classic/element/ReactElementValidator.js +++ b/src/classic/element/ReactElementValidator.js @@ -24,6 +24,7 @@ var ReactCurrentOwner = require('ReactCurrentOwner'); var getIteratorFn = require('getIteratorFn'); var monitorCodeUse = require('monitorCodeUse'); +var warning = require('warning'); /** * Warn if there's no key explicitly set on dynamic arrays of children or @@ -239,6 +240,15 @@ function checkPropTypes(componentName, propTypes, props, location) { var ReactElementValidator = { createElement: function(type, props, children) { + // We warn in this case but don't throw. We expect the element creation to + // succeed and there will likely be errors in render. + warning( + type != null, + 'React.createElement: type should not be null or undefined. It should ' + + 'be a string (for DOM elements) or a ReactClass (for composite ' + + 'components).' + ); + var element = ReactElement.createElement.apply(this, arguments); // The result can be nullish if a mock or a custom function is used. @@ -251,22 +261,24 @@ var ReactElementValidator = { validateChildKeys(arguments[i], type); } - var name = type.displayName; - if (type.propTypes) { - checkPropTypes( - name, - type.propTypes, - element.props, - ReactPropTypeLocations.prop - ); - } - if (type.contextTypes) { - checkPropTypes( - name, - type.contextTypes, - element._context, - ReactPropTypeLocations.context - ); + if (type) { + var name = type.displayName; + if (type.propTypes) { + checkPropTypes( + name, + type.propTypes, + element.props, + ReactPropTypeLocations.prop + ); + } + if (type.contextTypes) { + checkPropTypes( + name, + type.contextTypes, + element._context, + ReactPropTypeLocations.context + ); + } } return element; }, diff --git a/src/classic/element/__tests__/ReactElementValidator-test.js b/src/classic/element/__tests__/ReactElementValidator-test.js index 490dc9182fd..67198ac6055 100644 --- a/src/classic/element/__tests__/ReactElementValidator-test.js +++ b/src/classic/element/__tests__/ReactElementValidator-test.js @@ -55,6 +55,28 @@ describe('ReactElementValidator', function() { ); }); + it('gives a helpful error when passing null or undefined', function() { + spyOn(console, 'warn'); + React.createElement(undefined); + React.createElement(null); + expect(console.warn.calls.length).toBe(2); + expect(console.warn.calls[0].args[0]).toBe( + 'Warning: React.createElement: type should not be null or undefined. ' + + 'It should be a string (for DOM elements) or a ReactClass (for ' + + 'composite components).' + ); + expect(console.warn.calls[1].args[0]).toBe( + 'Warning: React.createElement: type should not be null or undefined. ' + + 'It should be a string (for DOM elements) or a ReactClass (for ' + + 'composite components).' + ); + React.createElement('div'); + expect(console.warn.calls.length).toBe(2); + + expect(() => React.createElement(undefined)).not.toThrow() + }); + + it('should check default prop values', function() { spyOn(console, 'warn');