Skip to content

Commit

Permalink
Warns on access of props.key and props.ref
Browse files Browse the repository at this point in the history
  • Loading branch information
ManasJayanth committed Feb 2, 2016
1 parent ae5ff24 commit 55607a8
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 3 deletions.
58 changes: 55 additions & 3 deletions src/isomorphic/classic/element/ReactElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
var ReactCurrentOwner = require('ReactCurrentOwner');

var assign = require('Object.assign');
var warning = require('warning');
var canDefineProperty = require('canDefineProperty');

// The Symbol used to tag the ReactElement type. If there is no native Symbol
Expand All @@ -29,6 +30,8 @@ var RESERVED_PROPS = {
__source: true,
};

var specialPropKeyWarningShown, specialPropRefWarningShown;

/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, no instanceof check
Expand Down Expand Up @@ -123,8 +126,15 @@ ReactElement.createElement = function(type, config, children) {
var source = null;

if (config != null) {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : '' + config.key;
if (__DEV__) {
ref = !config.hasOwnProperty('ref') ||
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
key = !config.hasOwnProperty('key') ||
Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
} else {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
Expand Down Expand Up @@ -158,7 +168,49 @@ ReactElement.createElement = function(type, config, children) {
}
}
}

if (__DEV__) {
// Create dummy `key` and `ref` property to `props` to warn users
// against its use
if (typeof props.$$typeof === 'undefined' ||
props.$$typeof !== REACT_ELEMENT_TYPE) {
if (!props.hasOwnProperty('key')) {
Object.defineProperty(props, 'key', {
get: function() {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
warning(
false,
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
'displayName' in type ? type.displayName: 'Element'
);
}
},
configurable: true,
});
}
if (!props.hasOwnProperty('ref')) {
Object.defineProperty(props, 'ref', {
get: function() {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
warning(
false,
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
'displayName' in type ? type.displayName: 'Element'
);
}
},
configurable: true,
});
}
}
}
return ReactElement(
type,
key,
Expand Down
58 changes: 58 additions & 0 deletions src/isomorphic/classic/element/__tests__/ReactElement-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,64 @@ describe('ReactElement', function() {
expect(element.props).toEqual(expectation);
});

it('should warn when `key` is being accessed', function() {
spyOn(console, 'error');
var container = document.createElement('div');
var Child = React.createClass({
render: function() {
return <div> {this.props.key} </div>;
},
});
var Parent = React.createClass({
render: function() {
return (
<div>
<Child key="0" />
<Child key="1" />
<Child key="2" />
</div>
);
},
});
expect(console.error.calls.length).toBe(0);
ReactDOM.render(<Parent />, container);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'Child: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)'
);
});

it('should warn when `ref` is being accessed', function() {
spyOn(console, 'error');
var container = document.createElement('div');
var Child = React.createClass({
render: function() {
return <div> {this.props.ref} </div>;
},
});
var Parent = React.createClass({
render: function() {
return (
<div>
<Child ref="childElement" />
</div>
);
},
});
expect(console.error.calls.length).toBe(0);
ReactDOM.render(<Parent />, container);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'Child: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)'
);
});

it('allows a string to be passed as the type', function() {
var element = React.createFactory('div')();
expect(element.type).toBe('div');
Expand Down

0 comments on commit 55607a8

Please sign in to comment.