21
var ReactContext = require('ReactContext');
22
var ReactCurrentOwner = require('ReactCurrentOwner');
23
24
-var merge = require('merge');
24
var warning = require('warning');
25
26
+var RESERVED_PROPS = {
27
+ key: true,
28
+ ref: true
29
+};
30
+
31
/**
32
* Warn for mutations.
33
*
85
}
86
}
87
84
-/**
85
- * Transfer static properties from the source to the target. Functions are
86
- * rebound to have this reflect the original source.
87
- */
88
-function proxyStaticMethods(target, source) {
89
- if (typeof source !== 'function') {
90
- return;
91
- }
92
- for (var key in source) {
93
- if (source.hasOwnProperty(key)) {
94
- var value = source[key];
95
- if (typeof value === 'function') {
96
- var bound = value.bind(source);
97
- // Copy any properties defined on the function, such as `isRequired` on
98
- // a PropTypes validator. (mergeInto refuses to work on functions.)
99
- for (var k in value) {
100
- if (value.hasOwnProperty(k)) {
101
- bound[k] = value[k];
102
- }
103
- }
104
- target[key] = bound;
105
- } else {
106
- target[key] = value;
107
- }
108
- }
109
- }
110
-}
111
-
88
/**
89
* Base constructor for all React descriptors. This is only used to make this
90
* work with a dynamic instanceof check. Nothing should live on this prototype.
91
*
92
* @param {*} type
93
+ * @param {string|object} ref
94
+ * @param {*} key
95
+ * @params {*} props
96
* @internal
97
*/
119
-var ReactDescriptor = function() {};
98
+var ReactDescriptor = function(type, key, ref, owner, context, props) {
99
+ // Built-in properties that belong on the descriptor
100
+ this.type = type;
101
+ this.key = key;
102
+ this.ref = ref;
103
+
104
+ // Record the component responsible for creating this descriptor.
105
+ this._owner = owner;
106
+
107
+ // TODO: Deprecate withContext, and then the context becomes accessible
108
+ // through the owner.
109
+ this._context = context;
110
+
111
+ if (__DEV__) {
112
+ // The validation flag and props are currently mutative. We put them on
113
+ // an external backing store so that we can freeze the whole object.
114
+ // This can be replaced with a WeakMap once they are implemented in
115
+ // commonly used development environments.
116
+ this._store = { validated: false, props: props };
117
+
118
+ // We're not allowed to set props directly on the object so we early
119
+ // return and rely on the prototype membrane to forward to the backing
120
+ // store.
121
+ if (useMutationMembrane) {
122
+ Object.freeze(this);
123
+ return;
124
+ }
125
+ }
126
+
127
+ this.props = props;
128
+};
129
130
if (__DEV__) {
131
defineMutationMembrane(ReactDescriptor.prototype);
132
}
133
125
-ReactDescriptor.createFactory = function(type) {
126
-
127
- var descriptorPrototype = Object.create(ReactDescriptor.prototype);
128
-
129
- var factory = function(props, children) {
130
- // For consistency we currently allocate a new object for every descriptor.
131
- // This protects the descriptor from being mutated by the original props
132
- // object being mutated. It also protects the original props object from
133
- // being mutated by children arguments and default props. This behavior
134
- // comes with a performance cost and could be deprecated in the future.
135
- // It could also be optimized with a smarter JSX transform.
136
- if (props == null) {
137
- props = {};
138
- } else if (typeof props === 'object') {
139
- props = merge(props);
140
- }
134
+ReactDescriptor.createDescriptor = function(type, config, children) {
135
+ if (type.isReactLegacyFactory) {
136
+ // This is probably a legacy factory created by ReactCompositeComponent.
137
+ // We unwrap it to get to the underlying class.
138
+ // TODO: Drop this check when we drop ReactLegacyDescriptor.
139
+ type = type.type;
140
+ }
141
142
- // Children can be more than one argument, and those are transferred onto
143
- // the newly allocated props object.
144
- var childrenLength = arguments.length - 1;
145
- if (childrenLength === 1) {
146
- props.children = children;
147
- } else if (childrenLength > 1) {
148
- var childArray = Array(childrenLength);
149
- for (var i = 0; i < childrenLength; i++) {
150
- childArray[i] = arguments[i + 1];
142
+ if (typeof type === 'function') {
143
+ var isReactClass =
144
+ typeof type.prototype.mountComponent === 'function' &&
145
+ typeof type.prototype.receiveComponent === 'function';
146
+ if (!isReactClass) {
147
+ // This is being called with a plain function we should invoke it
148
+ // immediately as if this was used with legacy JSX.
149
+ if (__DEV__) {
150
+ warning(
151
+ false,
152
+ 'This JSX uses a plain function. Only React components are valid in' +
153
+ 'JSX.'
154
+ );
155
}
152
- props.children = childArray;
156
+ var args = Array.prototype.slice.call(arguments, 1);
157
+ return type.apply(null, args);
158
}
159
+ }
160
+
161
+ // Reserved names are extracted
162
+ var props = {};
163
155
- // Initialize the descriptor object
156
- var descriptor = Object.create(descriptorPrototype);
157
-
158
- // Record the component responsible for creating this descriptor.
159
- descriptor._owner = ReactCurrentOwner.current;
160
-
161
- // TODO: Deprecate withContext, and then the context becomes accessible
162
- // through the owner.
163
- descriptor._context = ReactContext.current;
164
-
165
- if (__DEV__) {
166
- // The validation flag and props are currently mutative. We put them on
167
- // an external backing store so that we can freeze the whole object.
168
- // This can be replaced with a WeakMap once they are implemented in
169
- // commonly used development environments.
170
- descriptor._store = { validated: false, props: props };
171
-
172
- // We're not allowed to set props directly on the object so we early
173
- // return and rely on the prototype membrane to forward to the backing
174
- // store.
175
- if (useMutationMembrane) {
176
- Object.freeze(descriptor);
177
- return descriptor;
164
+ var key = null;
165
+ var ref = null;
166
+
167
+ if (config != null) {
168
+ ref = config.ref === undefined ? null : config.ref;
169
+ key = config.key === undefined ? null : '' + config.key;
170
+ // Remaining properties are added to a new props object
171
+ for (var propName in config) {
172
+ if (config.hasOwnProperty(propName) &&
173
+ !RESERVED_PROPS.hasOwnProperty(propName)) {
174
+ props[propName] = config[propName];
175
}
176
}
177
+ }
178
181
- descriptor.props = props;
182
- return descriptor;
183
- };
179
+ // TODO: fill in defaultProps here, after transferPropsTo is gone
180
+
181
+ // Children can be more than one argument, and those are transferred onto
182
+ // the newly allocated props object.
183
+ var childrenLength = arguments.length - 2;
184
+ if (childrenLength === 1) {
185
+ props.children = children;
186
+ } else if (childrenLength > 1) {
187
+ var childArray = Array(childrenLength);
188
+ for (var i = 0; i < childrenLength; i++) {
189
+ childArray[i] = arguments[i + 2];
190
+ }
191
+ props.children = childArray;
192
+ }
193
185
- // Currently we expose the prototype of the descriptor so that
186
- // <Foo /> instanceof Foo works. This is controversial pattern.
187
- factory.prototype = descriptorPrototype;
194
+ return new ReactDescriptor(
195
+ type,
196
+ key,
197
+ ref,
198
+ ReactCurrentOwner.current,
199
+ ReactContext.current,
200
+ props
201
+ );
202
+};
203
204
+ReactDescriptor.createFactory = function(type) {
205
+ if (type.isReactLegacyFactory) {
206
+ // This is probably a legacy factory created by ReactCompositeComponent.
207
+ // We unwrap it to get to the underlying class.
208
+ // TODO: Drop this check when we drop ReactLegacyDescriptor.
209
+ type = type.type;
210
+ }
211
+ var factory = ReactDescriptor.createDescriptor.bind(null, type);
212
// Expose the type on the factory and the prototype so that it can be
190
- // easily accessed on descriptors. E.g. <Foo />.type === Foo.type and for
191
- // static methods like <Foo />.type.staticMethod();
192
- // This should not be named constructor since this may not be the function
213
+ // easily accessed on descriptors. E.g. <Foo />.type === Foo.type.
214
+ // This should not be named `constructor` since this may not be the function
215
// that created the descriptor, and it may not even be a constructor.
216
factory.type = type;
195
- descriptorPrototype.type = type;
196
-
197
- proxyStaticMethods(factory, type);
198
-
199
- // Expose a unique constructor on the prototype is that this works with type
200
- // systems that compare constructor properties: <Foo />.constructor === Foo
201
- // This may be controversial since it requires a known factory function.
202
- descriptorPrototype.constructor = factory;
203
-
217
return factory;
205
-
218
};
219
220
ReactDescriptor.cloneAndReplaceProps = function(oldDescriptor, newProps) {
209
- var newDescriptor = Object.create(oldDescriptor.constructor.prototype);
210
- // It's important that this property order matches the hidden class of the
211
- // original descriptor to maintain perf.
212
- newDescriptor._owner = oldDescriptor._owner;
213
- newDescriptor._context = oldDescriptor._context;
221
+ var newDescriptor = new ReactDescriptor(
222
+ oldDescriptor.type,
223
+ oldDescriptor.key,
224
+ oldDescriptor.ref,
225
+ oldDescriptor._owner,
226
+ oldDescriptor._context,
227
+ newProps
228
+ );
229
230
if (__DEV__) {
216
- newDescriptor._store = {
217
- validated: oldDescriptor._store.validated,
218
- props: newProps
219
- };
220
- if (useMutationMembrane) {
221
- Object.freeze(newDescriptor);
222
- return newDescriptor;
223
- }
231
+ // If the key on the original is valid, then the clone is valid
232
+ newDescriptor._store.validated = oldDescriptor._store.validated;
233
}
225
-
226
- newDescriptor.props = newProps;
234
return newDescriptor;
235
};
236
242
* @public
243
*/
244
ReactDescriptor.isValidFactory = function(factory) {
238
- return typeof factory === 'function' &&
239
- factory.prototype instanceof ReactDescriptor;
245
+ return typeof factory === 'function' &&
246
+ typeof factory.type === 'function' &&
247
+ typeof factory.type.prototype.mountComponent === 'function' &&
248
+ typeof factory.type.prototype.receiveComponent === 'function';
249
};
250
251
/**