mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
This adds ReactProps.func so people don't need to write the slightly-more-cryptic ReactProps.instanceOf(Function). We should have had this all along.
159 lines
4.7 KiB
JavaScript
159 lines
4.7 KiB
JavaScript
/**
|
|
* 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.
|
|
*
|
|
* @providesModule ReactProps
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var createObjectFrom = require('createObjectFrom');
|
|
var invariant = require('invariant');
|
|
|
|
/**
|
|
* Collection of methods that allow declaration and validation of props that are
|
|
* supplied to React components. Example usage:
|
|
*
|
|
* var Props = require('ReactProps');
|
|
* var MyArticle = React.createClass({
|
|
* props: {
|
|
* // An optional string prop named "description".
|
|
* description: Props.string,
|
|
*
|
|
* // A required enum prop named "category".
|
|
* category: Props.oneOf(['News','Photos']).isRequired,
|
|
*
|
|
* // A prop named "dialog" that requires an instance of Dialog.
|
|
* dialog: Props.instanceOf(Dialog).isRequired
|
|
* },
|
|
* render: function() { ... }
|
|
* });
|
|
*
|
|
* A more formal specification of how these methods are used:
|
|
*
|
|
* type := array|bool|object|number|string|oneOf([...])|instanceOf(...)
|
|
* decl := ReactProps.{type}(.isRequired)?
|
|
*
|
|
* Each and every declaration produces a function with the same signature. This
|
|
* allows the creation of custom validation functions. For example:
|
|
*
|
|
* var Props = require('ReactProps');
|
|
* var MyLink = React.createClass({
|
|
* props: {
|
|
* // An optional string or URI prop named "href".
|
|
* href: function(props, propName, componentName) {
|
|
* var propValue = props[propName];
|
|
* invariant(
|
|
* propValue == null ||
|
|
* typeof propValue === string ||
|
|
* propValue instanceof URI,
|
|
* 'Invalid `%s` supplied to `%s`, expected string or URI.',
|
|
* propName,
|
|
* componentName
|
|
* );
|
|
* }
|
|
* },
|
|
* render: function() { ... }
|
|
* });
|
|
*
|
|
* @internal
|
|
*/
|
|
var Props = {
|
|
|
|
array: createPrimitiveTypeChecker('array'),
|
|
bool: createPrimitiveTypeChecker('boolean'),
|
|
func: createPrimitiveTypeChecker('function'),
|
|
number: createPrimitiveTypeChecker('number'),
|
|
object: createPrimitiveTypeChecker('object'),
|
|
string: createPrimitiveTypeChecker('string'),
|
|
|
|
oneOf: createEnumTypeChecker,
|
|
|
|
instanceOf: createInstanceTypeChecker
|
|
|
|
};
|
|
|
|
var ANONYMOUS = '<<anonymous>>';
|
|
|
|
function createPrimitiveTypeChecker(expectedType) {
|
|
function validatePrimitiveType(propValue, propName, componentName) {
|
|
var propType = typeof propValue;
|
|
if (propType === 'object' && Array.isArray(propValue)) {
|
|
propType = 'array';
|
|
}
|
|
invariant(
|
|
propType === expectedType,
|
|
'Invalid prop `%s` of type `%s` supplied to `%s`, expected `%s`.',
|
|
propName,
|
|
propType,
|
|
componentName,
|
|
expectedType
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validatePrimitiveType);
|
|
}
|
|
|
|
function createEnumTypeChecker(expectedValues) {
|
|
var expectedEnum = createObjectFrom(expectedValues);
|
|
function validateEnumType(propValue, propName, componentName) {
|
|
invariant(
|
|
expectedEnum[propValue],
|
|
'Invalid prop `%s` supplied to `%s`, expected one of %s.',
|
|
propName,
|
|
componentName,
|
|
JSON.stringify(Object.keys(expectedEnum))
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validateEnumType);
|
|
}
|
|
|
|
function createInstanceTypeChecker(expectedClass) {
|
|
function validateInstanceType(propValue, propName, componentName) {
|
|
invariant(
|
|
propValue instanceof expectedClass,
|
|
'Invalid prop `%s` supplied to `%s`, expected instance of `%s`.',
|
|
propName,
|
|
componentName,
|
|
expectedClass.name || ANONYMOUS
|
|
);
|
|
}
|
|
return createChainableTypeChecker(validateInstanceType);
|
|
}
|
|
|
|
function createChainableTypeChecker(validate) {
|
|
function createTypeChecker(isRequired) {
|
|
function checkType(props, propName, componentName) {
|
|
var propValue = props[propName];
|
|
if (propValue != null) {
|
|
// Only validate if there is a value to check.
|
|
validate(propValue, propName, componentName || ANONYMOUS);
|
|
} else {
|
|
invariant(
|
|
!isRequired,
|
|
'Required prop `%s` was not specified in `%s`.',
|
|
propName,
|
|
componentName || ANONYMOUS
|
|
);
|
|
}
|
|
}
|
|
if (!isRequired) {
|
|
checkType.isRequired = createTypeChecker(true);
|
|
}
|
|
return checkType;
|
|
}
|
|
return createTypeChecker(false);
|
|
}
|
|
|
|
module.exports = Props;
|