Skip to content

Commit 55607a8

Browse files
committed
Warns on access of props.key and props.ref
1 parent ae5ff24 commit 55607a8

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed

src/isomorphic/classic/element/ReactElement.js

+55-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
var ReactCurrentOwner = require('ReactCurrentOwner');
1515

1616
var assign = require('Object.assign');
17+
var warning = require('warning');
1718
var canDefineProperty = require('canDefineProperty');
1819

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

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

125128
if (config != null) {
126-
ref = config.ref === undefined ? null : config.ref;
127-
key = config.key === undefined ? null : '' + config.key;
129+
if (__DEV__) {
130+
ref = !config.hasOwnProperty('ref') ||
131+
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
132+
key = !config.hasOwnProperty('key') ||
133+
Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
134+
} else {
135+
ref = config.ref === undefined ? null : config.ref;
136+
key = config.key === undefined ? null : '' + config.key;
137+
}
128138
self = config.__self === undefined ? null : config.__self;
129139
source = config.__source === undefined ? null : config.__source;
130140
// Remaining properties are added to a new props object
@@ -158,7 +168,49 @@ ReactElement.createElement = function(type, config, children) {
158168
}
159169
}
160170
}
161-
171+
if (__DEV__) {
172+
// Create dummy `key` and `ref` property to `props` to warn users
173+
// against its use
174+
if (typeof props.$$typeof === 'undefined' ||
175+
props.$$typeof !== REACT_ELEMENT_TYPE) {
176+
if (!props.hasOwnProperty('key')) {
177+
Object.defineProperty(props, 'key', {
178+
get: function() {
179+
if (!specialPropKeyWarningShown) {
180+
specialPropKeyWarningShown = true;
181+
warning(
182+
false,
183+
'%s: `key` is not a prop. Trying to access it will result ' +
184+
'in `undefined` being returned. If you need to access the same ' +
185+
'value within the child component, you should pass it as a different ' +
186+
'prop. (https://fb.me/react-special-props)',
187+
'displayName' in type ? type.displayName: 'Element'
188+
);
189+
}
190+
},
191+
configurable: true,
192+
});
193+
}
194+
if (!props.hasOwnProperty('ref')) {
195+
Object.defineProperty(props, 'ref', {
196+
get: function() {
197+
if (!specialPropRefWarningShown) {
198+
specialPropRefWarningShown = true;
199+
warning(
200+
false,
201+
'%s: `ref` is not a prop. Trying to access it will result ' +
202+
'in `undefined` being returned. If you need to access the same ' +
203+
'value within the child component, you should pass it as a different ' +
204+
'prop. (https://fb.me/react-special-props)',
205+
'displayName' in type ? type.displayName: 'Element'
206+
);
207+
}
208+
},
209+
configurable: true,
210+
});
211+
}
212+
}
213+
}
162214
return ReactElement(
163215
type,
164216
key,

src/isomorphic/classic/element/__tests__/ReactElement-test.js

+58
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,64 @@ describe('ReactElement', function() {
5858
expect(element.props).toEqual(expectation);
5959
});
6060

61+
it('should warn when `key` is being accessed', function() {
62+
spyOn(console, 'error');
63+
var container = document.createElement('div');
64+
var Child = React.createClass({
65+
render: function() {
66+
return <div> {this.props.key} </div>;
67+
},
68+
});
69+
var Parent = React.createClass({
70+
render: function() {
71+
return (
72+
<div>
73+
<Child key="0" />
74+
<Child key="1" />
75+
<Child key="2" />
76+
</div>
77+
);
78+
},
79+
});
80+
expect(console.error.calls.length).toBe(0);
81+
ReactDOM.render(<Parent />, container);
82+
expect(console.error.calls.length).toBe(1);
83+
expect(console.error.argsForCall[0][0]).toContain(
84+
'Child: `key` is not a prop. Trying to access it will result ' +
85+
'in `undefined` being returned. If you need to access the same ' +
86+
'value within the child component, you should pass it as a different ' +
87+
'prop. (https://fb.me/react-special-props)'
88+
);
89+
});
90+
91+
it('should warn when `ref` is being accessed', function() {
92+
spyOn(console, 'error');
93+
var container = document.createElement('div');
94+
var Child = React.createClass({
95+
render: function() {
96+
return <div> {this.props.ref} </div>;
97+
},
98+
});
99+
var Parent = React.createClass({
100+
render: function() {
101+
return (
102+
<div>
103+
<Child ref="childElement" />
104+
</div>
105+
);
106+
},
107+
});
108+
expect(console.error.calls.length).toBe(0);
109+
ReactDOM.render(<Parent />, container);
110+
expect(console.error.calls.length).toBe(1);
111+
expect(console.error.argsForCall[0][0]).toContain(
112+
'Child: `ref` is not a prop. Trying to access it will result ' +
113+
'in `undefined` being returned. If you need to access the same ' +
114+
'value within the child component, you should pass it as a different ' +
115+
'prop. (https://fb.me/react-special-props)'
116+
);
117+
});
118+
61119
it('allows a string to be passed as the type', function() {
62120
var element = React.createFactory('div')();
63121
expect(element.type).toBe('div');

0 commit comments

Comments
 (0)