Warns on access of `props.key` and `props.ref`
Manas committed
Dec 29, 2015 at 15:40 UTC
c3980a6d08257d39a08bed8b116d53a56344166b
2 files changed
+115
-3
src/isomorphic/classic/element/ReactElement.js
+57
-3
@@ -14,6 +14,7 @@
14
var ReactCurrentOwner = require('ReactCurrentOwner');
15
16
var assign = require('Object.assign');
17
+var warning = require('warning');
18
var canDefineProperty = require('canDefineProperty');
19
20
// The Symbol used to tag the ReactElement type. If there is no native Symbol
@@ -29,6 +30,8 @@ var RESERVED_PROPS = {
30
__source: true,
31
};
32
33
+var specialPropKeyWarningShown, specialPropRefWarningShown;
34
+
35
/**
36
* Factory method to create a new React element. This no longer adheres to
37
* 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) {
126
var source = null;
127
128
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
+ }
138
self = config.__self === undefined ? null : config.__self;
139
source = config.__source === undefined ? null : config.__source;
140
// Remaining properties are added to a new props object
@@ -158,7 +168,51 @@ ReactElement.createElement = function(type, config, children) {
168
}
169
}
170
}
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
+ return undefined;
191
+ },
192
+ configurable: true,
193
+ });
194
+ }
195
+ if (!props.hasOwnProperty('ref')) {
196
+ Object.defineProperty(props, 'ref', {
197
+ get: function() {
198
+ if (!specialPropRefWarningShown) {
199
+ specialPropRefWarningShown = true;
200
+ warning(
201
+ false,
202
+ '%s: `ref` is not a prop. Trying to access it will result ' +
203
+ 'in `undefined` being returned. If you need to access the same ' +
204
+ 'value within the child component, you should pass it as a different ' +
205
+ 'prop. (https://fb.me/react-special-props)',
206
+ 'displayName' in type ? type.displayName: 'Element'
207
+ );
208
+ }
209
+ return undefined;
210
+ },
211
+ configurable: true,
212
+ });
213
+ }
214
+ }
215
+ }
216
return ReactElement(
217
type,
218
key,
src/isomorphic/classic/element/__tests__/ReactElement-test.js
+58
@@ -58,6 +58,64 @@ describe('ReactElement', function() {
58
expect(element.props).toEqual(expectation);
59
});
60
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
+
119
it('allows a string to be passed as the type', function() {
120
var element = React.createFactory('div')();
121
expect(element.type).toBe('div');