mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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:
committed by
Paul O’Shannessy
parent
1457850b72
commit
ca5d7bc683
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user