Add getDefaultProps()

As it turns out, default values are very useful. This implements
getDefaultProps(), a hook for components to provide prop values when
a prop is not specified by the user.
This commit is contained in:
CommitSyncScript
2013-06-06 14:29:44 -07:00
committed by Paul O’Shannessy
parent 1457850b72
commit ca5d7bc683
2 changed files with 91 additions and 21 deletions
+44 -21
View File
@@ -91,6 +91,19 @@ var ReactCompositeComponentInterface = {
// ==== Definition methods ====
/**
* Invoked when the component is mounted and whenever new props are received.
* Values in the returned mapping will be set on `this.props` if that prop is
* not specified (i.e. using an `in` check).
*
* This method is invoked before `getInitialState` and therefore cannot rely
* on `this.state` or use `this.setState`.
*
* @return {object}
* @optional
*/
getDefaultProps: SpecPolicy.DEFINE_ONCE,
/**
* Invoked once before the component is mounted. The return value will be used
* as the initial value of `this.state`.
@@ -419,9 +432,7 @@ var ReactCompositeComponentMixin = {
this._lifeCycleState = ReactComponent.LifeCycle.UNMOUNTED;
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
if (this.constructor.propDeclarations) {
this._assertValidProps(this.props);
}
this._processProps(this.props);
if (this.__reactAutoBindMap) {
this._bindAutoBindMethods();
@@ -489,9 +500,7 @@ var ReactCompositeComponentMixin = {
* @internal
*/
receiveProps: function(nextProps, transaction) {
if (this.constructor.propDeclarations) {
this._assertValidProps(nextProps);
}
this._processProps(nextProps);
ReactComponent.Mixin.receiveProps.call(this, nextProps, transaction);
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
@@ -573,6 +582,35 @@ var ReactCompositeComponentMixin = {
}
},
/**
* Processes props by setting default values for unspecified props and
* asserting that the props are valid.
*
* @param {object} props
* @private
*/
_processProps: function(props) {
var propName;
if (this.getDefaultProps) {
var defaultProps = this.getDefaultProps();
for (propName in defaultProps) {
if (!(propName in props)) {
props[propName] = defaultProps[propName];
}
}
}
var propDeclarations = this.constructor.propDeclarations;
if (propDeclarations) {
var componentName = this.constructor.displayName;
for (propName in propDeclarations) {
var checkProp = propDeclarations[propName];
if (checkProp) {
checkProp(props, propName, componentName);
}
}
}
},
/**
* Receives next props and next state, and negotiates whether or not the
* component should update as a result.
@@ -696,21 +734,6 @@ var ReactCompositeComponentMixin = {
return renderedComponent;
},
/**
* @param {object} props
* @private
*/
_assertValidProps: function(props) {
var propDeclarations = this.constructor.propDeclarations;
var componentName = this.constructor.displayName;
for (var propName in propDeclarations) {
var checkProp = propDeclarations[propName];
if (checkProp) {
checkProp(props, propName, componentName);
}
}
},
/**
* @private
*/
@@ -194,6 +194,53 @@ describe('ReactCompositeComponent', function() {
expect(retValAfterMountWithCrazyScope).toBe(RETURN_VALUE_AFTER_MOUNT);
});
it('should normalize props with default values', function() {
var Component = React.createClass({
props: {key: ReactProps.string.isRequired},
getDefaultProps: function() {
return {key: 'testKey'};
},
getInitialState: function() {
return {key: this.props.key + 'State'};
},
render: function() {
return <span>{this.props.key}</span>;
}
});
var instance = <Component />;
ReactTestUtils.renderIntoDocument(instance);
reactComponentExpect(instance).scalarPropsEqual({key: 'testKey'});
reactComponentExpect(instance).scalarStateEqual({key: 'testKeyState'});
expect(function() {
ReactTestUtils.renderIntoDocument(<Component key={null} />);
}).toThrow(
'Invariant Violation: Required prop `key` was not specified in ' +
'`Component`.'
);
});
it('should check default prop values', function() {
var Component = React.createClass({
props: {key: ReactProps.string.isRequired},
getDefaultProps: function() {
return {key: null};
},
render: function() {
return <span>{this.props.key}</span>;
}
});
var instance = <Component />;
expect(function() {
ReactTestUtils.renderIntoDocument(instance);
}).toThrow(
'Invariant Violation: Required prop `key` was not specified in ' +
'`Component`.'
);
});
it('should check declared prop types', function() {
var Component = React.createClass({
props: {