注意:该答案写于2015年,当前的React版本是0.14.3。它可能不适用于您今天使用的React版本。
这是一个有趣的问题。从您的问题看来,您似乎已经在Prop Validation的文档中阅读了有关自定义类型检查器的信息。为了后代,我将在此处复制:
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
在实现类型检查器时,我更喜欢尽可能使用React的内置类型检查器。您想检查值是否为数字,所以我们应该使用PropTypes.number
它,对吗?如果我们可以做PropTypes.number('not a number!')
并得到适当的错误,那将是很好的,但是不幸的是,它涉及的更多。首先是要了解...
类型检查器如何工作
这是类型检查器的功能签名:
function(props, propName, componentName, location, propFullName) => null | Error
如您所见,所有道具都作为第一个参数传递,被测试道具的名称作为第二个参数传递。最后三个参数用于打印出有用的错误消息,并且是可选的:componentName
不言自明。location
将是一个
'prop'
,'context'
或者'childContext'
(我们只关心
'prop'
),并且propFullName
是当我们正在处理嵌套的道具,例如用于 someObj.someKey
。
有了这些知识,我们现在可以直接调用类型检查器:
PropTypes.number({ myProp: 'bad' }, 'myProp');
看到?没有所有的论点就没有那么有用了。这个更好:
PropTypes.number({ myProp: 'bad' }, 'myProp', 'MyComponent', 'prop')
数组类型检查器
文档没有提及的一件事是,当您向提供一个自定义类型检查器时PropTypes.arrayOf
,将为每个数组元素调用该方法,并且前两个参数分别是数组本身和当前元素的索引。现在我们可以开始草绘类型检查器了:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
console.log(propFullName, obj);
return null;
}
到目前为止,它总是会返回null
(指示有效的道具),但是我们投入了一个console.log
以了解发生了什么。现在我们可以像这样测试它:
var typeChecker = PropTypes.arrayOf(validArrayItem);
var myArray = [ { foo: 1 }, { bar: 'qux' } ];
var props = { myProp: myArray };
typeChecker(props, 'myProp', 'MyComponent', 'prop');
如您所见,propFullName
是myProp[0]
第一项,
myProp[1]
第二项。
现在让我们充实函数的三个部分。
1.检查是否obj
是使用PropTypes.object
这是最简单的部分:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
return null;
}
var typeChecker = PropTypes.arrayOf(validArrayItem);
var props = { myProp: [ { foo: 1 }, 'bar' ] };
typeChecker(props, 'myProp', 'MyComponent', 'prop');
完善!下一个...
2.检查其所有密钥是否均符合某些指定格式
在您的问题中,您说“每个键应该是一个字符串”,但是JavaScript中的所有对象键都是字符串,因此,我们可以随意地说,我们要测试这些键是否都以大写字母开头。让我们为此做一个自定义类型检查器:
var STARTS_WITH_UPPERCASE_LETTER_EXPR = /^[A-Z]/;
function validObjectKeys(props, propName, componentName, location, propFullName) {
var obj = props[propName];
var keys = Object.keys(obj);
if (keys.length === 0) { return null; }
var key;
var propFullNameWithKey;
for (var i = 0; i < keys.length; i++) {
key = keys[i];
propFullNameWithKey = (propFullName || propName) + '.' + key;
if (STARTS_WITH_UPPERCASE_LETTER_EXPR.test(key)) { continue; }
return new Error(
'Invalid key `' + propFullNameWithKey + '` supplied to ' +
'`' + componentName + '`; expected to match ' +
STARTS_WITH_UPPERCASE_LETTER_EXPR + '.'
);
}
return null;
}
我们可以自己对其进行测试:
var props = { myProp: { Foo: 1, bar: 2 } };
validObjectKeys(props, 'myProp', 'MyComponent', 'prop');
大!让我们将其集成到validArrayItem
类型检查器中:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
var validObjectKeysError = validObjectKeys(props, propFullName, componentName);
if (validObjectKeysError) { return validObjectKeysError; }
return null;
}
并测试一下:
var props = { myProp: [ { Foo: 1 }, { bar: 2 } ] };
var typeChecker = PropTypes.arrayOf(validArrayItem);
typeChecker(props, 'myProp', 'MyComponent', 'prop');
最后...
3.检查其所有值是否均为数字
幸运的是,我们不需要在这里做很多工作,因为我们可以使用内置的PropTypes.objectOf
:
var validObjectValues = PropTypes.objectOf(PropTypes.number);
var validObjectValuesError = validObjectValues(props, propFullName, componentName, location);
if (validObjectValuesError) { return validObjectValuesError; }
我们将在下面对其进行测试。
现在都在一起了
这是我们的最终代码:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
var validObjectKeysError = validObjectKeys(props, propFullName, componentName);
if (validObjectKeysError) { return validObjectKeysError; }
var validObjectValues = PropTypes.objectOf(PropTypes.number);
var validObjectValuesError = validObjectValues(props, propFullName, componentName, location);
if (validObjectValuesError) { return validObjectValuesError; }
return null;
}
我们将编写一个用于测试的快速便捷功能,并向其中添加一些数据:
function test(arrayToTest) {
var typeChecker = PropTypes.arrayOf(validArrayItem);
var props = { testProp: arrayToTest };
return typeChecker(props, 'testProp', 'MyComponent', 'prop');
}
test([ { Foo: 1 }, { Bar: 2 } ]);
test([ { Foo: 1 }, { bar: 2 } ]);
test([ { Foo: 1 }, { Bar: false } ]);
有用!现在,您可以像内置类型检查器一样在React组件中使用它:
MyComponent.propTypes = {
someArray: PropTypes.arrayOf(validArrayItem);
};
当然,我建议给它起一个更有意义的名称,并将其移至其自己的模块中。
import { objectOf } from 'prop-types'