mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
cloneWithProps()
what if you want to change the props of a child? This is my first attempt which lets you clone a child and transfer some custom props to it. There is a fundamental issue with refs here. Since the component is cloned the ref will be broken. And since we can clone multiple times, it might not make sense to support repairing refs.
This commit is contained in:
committed by
Paul O’Shannessy
parent
5661d5168e
commit
cc005668b5
@@ -50,6 +50,8 @@ function createDOMComponentClass(tag, omitClose) {
|
||||
instance.construct.apply(instance, arguments);
|
||||
return instance;
|
||||
};
|
||||
|
||||
Constructor.ConvenienceConstructor = ConvenienceConstructor;
|
||||
ConvenienceConstructor.componentConstructor = Constructor;
|
||||
return ConvenienceConstructor;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,33 @@ var ReactPropTransferer = {
|
||||
|
||||
TransferStrategies: TransferStrategies,
|
||||
|
||||
/**
|
||||
* Merge two props objects using TransferStrategies.
|
||||
*
|
||||
* @param {object} oldProps original props (they take precedence)
|
||||
* @param {object} newProps new props to merge in
|
||||
* @return {object} a new object containing both sets of props merged.
|
||||
*/
|
||||
mergeProps: function(oldProps, newProps) {
|
||||
var props = merge(oldProps);
|
||||
|
||||
for (var thisKey in newProps) {
|
||||
if (!newProps.hasOwnProperty(thisKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var transferStrategy = TransferStrategies[thisKey];
|
||||
|
||||
if (transferStrategy) {
|
||||
transferStrategy(props, thisKey, newProps[thisKey]);
|
||||
} else if (!props.hasOwnProperty(thisKey)) {
|
||||
props[thisKey] = newProps[thisKey];
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
},
|
||||
|
||||
/**
|
||||
* @lends {ReactPropTransferer.prototype}
|
||||
*/
|
||||
@@ -104,29 +131,15 @@ var ReactPropTransferer = {
|
||||
component.constructor.displayName
|
||||
);
|
||||
|
||||
var props = {};
|
||||
for (var thatKey in component.props) {
|
||||
if (component.props.hasOwnProperty(thatKey)) {
|
||||
props[thatKey] = component.props[thatKey];
|
||||
}
|
||||
}
|
||||
for (var thisKey in this.props) {
|
||||
if (!this.props.hasOwnProperty(thisKey)) {
|
||||
continue;
|
||||
}
|
||||
var transferStrategy = TransferStrategies[thisKey];
|
||||
if (transferStrategy) {
|
||||
transferStrategy(props, thisKey, this.props[thisKey]);
|
||||
} else if (!props.hasOwnProperty(thisKey)) {
|
||||
props[thisKey] = this.props[thisKey];
|
||||
}
|
||||
}
|
||||
component.props = props;
|
||||
component.props = ReactPropTransferer.mergeProps(
|
||||
component.props,
|
||||
this.props
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactPropTransferer;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright 2013 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jsx React.DOM
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
require('mock-modules').dontMock('cloneWithProps');
|
||||
|
||||
var mocks = require('mocks');
|
||||
|
||||
var cloneWithProps = require('cloneWithProps');
|
||||
|
||||
var React;
|
||||
var ReactTestUtils;
|
||||
|
||||
var onlyChild;
|
||||
|
||||
describe('cloneWithProps', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
onlyChild = require('onlyChild');
|
||||
});
|
||||
|
||||
it('should clone an object with new props', function() {
|
||||
var Grandparent = React.createClass({
|
||||
render: function() {
|
||||
return <Parent><div className="child" /></Parent>;
|
||||
}
|
||||
});
|
||||
var Parent = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="parent">
|
||||
{cloneWithProps(onlyChild(this.props.children), {className: 'xyz'})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
var component = ReactTestUtils.renderIntoDocument(<Grandparent />);
|
||||
expect(component.getDOMNode().childNodes[0].className)
|
||||
.toBe('child xyz');
|
||||
});
|
||||
|
||||
it('should warn when cloning with refs', function() {
|
||||
var Grandparent = React.createClass({
|
||||
render: function() {
|
||||
return <Parent><div ref="yolo" /></Parent>;
|
||||
}
|
||||
});
|
||||
var Parent = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
{cloneWithProps(onlyChild(this.props.children), {className: 'xyz'})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var _warn = console.warn;
|
||||
|
||||
try {
|
||||
console.warn = mocks.getMockFunction();
|
||||
|
||||
var component = ReactTestUtils.renderIntoDocument(<Grandparent />);
|
||||
expect(component.refs).toBe(undefined);
|
||||
expect(console.warn.mock.calls.length).toBe(1);
|
||||
} finally {
|
||||
console.warn = _warn;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2013 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @typechecks
|
||||
* @providesModule cloneWithProps
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactPropTransferer = require('ReactPropTransferer');
|
||||
|
||||
/**
|
||||
* Sometimes you want to change the props of a child passed to you. Usually
|
||||
* this is to add a CSS class.
|
||||
*
|
||||
* @param {object} child child component you'd like to clone
|
||||
* @param {object} props props you'd like to modify. They will be merged
|
||||
* as if you used `transferPropsTo()`.
|
||||
* @return {object} a clone of child with props merged in.
|
||||
*/
|
||||
function cloneWithProps(child, props) {
|
||||
if (__DEV__) {
|
||||
if (child.props.ref) {
|
||||
console.warn(
|
||||
'You are calling cloneWithProps() on a child with a ref. This is ' +
|
||||
'dangerous because you\'re creating a new child which will not be ' +
|
||||
'added as a ref to its parent.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return child.constructor.ConvenienceConstructor(
|
||||
ReactPropTransferer.mergeProps(child.props, props)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = cloneWithProps;
|
||||
Reference in New Issue
Block a user