Skip to content

Commit ecc4ad5

Browse files
committed
Merge pull request facebook#3266 from sebmarkbage/cloneelement
Add cloneElement Implementation
2 parents edddd57 + 4adcee6 commit ecc4ad5

File tree

4 files changed

+371
-27
lines changed

4 files changed

+371
-27
lines changed

src/browser/ui/React.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ ReactDefaultInjection.inject();
3939

4040
var createElement = ReactElement.createElement;
4141
var createFactory = ReactElement.createFactory;
42+
var cloneElement = ReactElement.cloneElement;
4243

4344
if (__DEV__) {
4445
createElement = ReactElementValidator.createElement;
4546
createFactory = ReactElementValidator.createFactory;
47+
cloneElement = ReactElementValidator.cloneElement;
4648
}
4749

4850
var render = ReactPerf.measure('React', 'render', ReactMount.render);
@@ -62,6 +64,7 @@ var React = {
6264
},
6365
createClass: ReactClass.createClass,
6466
createElement: createElement,
67+
cloneElement: cloneElement,
6568
createFactory: createFactory,
6669
createMixin: function(mixin) {
6770
// Currently a noop. Will be used to validate and trace mixins.

src/classic/element/ReactElement.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,60 @@ ReactElement.cloneAndReplaceProps = function(oldElement, newProps) {
228228
return newElement;
229229
};
230230

231+
ReactElement.cloneElement = function(element, config, children) {
232+
var propName;
233+
234+
// Original props are copied
235+
var props = assign({}, element.props);
236+
237+
// Reserved names are extracted
238+
var key = element.key;
239+
var ref = element.ref;
240+
241+
// Owner will be preserved, unless ref is overridden
242+
var owner = element._owner;
243+
244+
if (config != null) {
245+
if (config.ref !== undefined) {
246+
// Silently steal the ref from the parent.
247+
ref = config.ref;
248+
owner = ReactCurrentOwner.current;
249+
}
250+
if (config.key !== undefined) {
251+
key = '' + config.key;
252+
}
253+
// Remaining properties override existing props
254+
for (propName in config) {
255+
if (config.hasOwnProperty(propName) &&
256+
!RESERVED_PROPS.hasOwnProperty(propName)) {
257+
props[propName] = config[propName];
258+
}
259+
}
260+
}
261+
262+
// Children can be more than one argument, and those are transferred onto
263+
// the newly allocated props object.
264+
var childrenLength = arguments.length - 2;
265+
if (childrenLength === 1) {
266+
props.children = children;
267+
} else if (childrenLength > 1) {
268+
var childArray = Array(childrenLength);
269+
for (var i = 0; i < childrenLength; i++) {
270+
childArray[i] = arguments[i + 2];
271+
}
272+
props.children = childArray;
273+
}
274+
275+
return new ReactElement(
276+
element.type,
277+
key,
278+
ref,
279+
owner,
280+
element._context,
281+
props
282+
);
283+
};
284+
231285
/**
232286
* @param {?object} object
233287
* @return {boolean} True if `object` is a valid component.

src/classic/element/ReactElementValidator.js

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,42 @@ function checkAndWarnForMutatedProps(element) {
336336
}
337337
}
338338

339+
/**
340+
* Given an element, validate that its props follow the propTypes definition,
341+
* provided by the type.
342+
*
343+
* @param {ReactElement} element
344+
*/
345+
function validatePropTypes(element) {
346+
if (element.type == null) {
347+
// This has already warned. Don't throw.
348+
return;
349+
}
350+
// Extract the component class from the element. Converts string types
351+
// to a composite class which may have propTypes.
352+
// TODO: Validating a string's propTypes is not decoupled from the
353+
// rendering target which is problematic.
354+
var componentClass = ReactNativeComponent.getComponentClassForElement(
355+
element
356+
);
357+
var name = componentClass.displayName || componentClass.name;
358+
if (componentClass.propTypes) {
359+
checkPropTypes(
360+
name,
361+
componentClass.propTypes,
362+
element.props,
363+
ReactPropTypeLocations.prop
364+
);
365+
}
366+
if (typeof componentClass.getDefaultProps === 'function') {
367+
warning(
368+
componentClass.getDefaultProps.isReactClassApproved,
369+
'getDefaultProps is only used on classic React.createClass ' +
370+
'definitions. Use a static property named `defaultProps` instead.'
371+
);
372+
}
373+
}
374+
339375
var ReactElementValidator = {
340376

341377
checkAndWarnForMutatedProps: checkAndWarnForMutatedProps,
@@ -362,33 +398,7 @@ var ReactElementValidator = {
362398
validateChildKeys(arguments[i], type);
363399
}
364400

365-
if (type) {
366-
// Extract the component class from the element. Converts string types
367-
// to a composite class which may have propTypes.
368-
// TODO: Validating a string's propTypes is not decoupled from the
369-
// rendering target which is problematic.
370-
var componentClass = ReactNativeComponent.getComponentClassForElement(
371-
element
372-
);
373-
var name = componentClass.displayName || componentClass.name;
374-
if (__DEV__) {
375-
if (componentClass.propTypes) {
376-
checkPropTypes(
377-
name,
378-
componentClass.propTypes,
379-
element.props,
380-
ReactPropTypeLocations.prop
381-
);
382-
}
383-
}
384-
if (typeof componentClass.getDefaultProps === 'function') {
385-
warning(
386-
componentClass.getDefaultProps.isReactClassApproved,
387-
'getDefaultProps is only used on classic React.createClass ' +
388-
'definitions. Use a static property named `defaultProps` instead.'
389-
);
390-
}
391-
}
401+
validatePropTypes(element);
392402

393403
return element;
394404
},
@@ -428,6 +438,15 @@ var ReactElementValidator = {
428438

429439

430440
return validatedFactory;
441+
},
442+
443+
cloneElement: function(element, props, children) {
444+
var newElement = ReactElement.cloneElement.apply(this, arguments);
445+
for (var i = 2; i < arguments.length; i++) {
446+
validateChildKeys(arguments[i], newElement.type);
447+
}
448+
validatePropTypes(newElement);
449+
return newElement;
431450
}
432451

433452
};

0 commit comments

Comments
 (0)