@samitouri / QOS-React / commits / c3980a6d08

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');