注意:这个答案是在 2015 年写的,当时 React 的当前版本是 0.14.3。它可能适用于您今天使用的 React 版本,也可能不适用。
这是一个有趣的问题。从您的问题来看,您似乎已经在Prop Validation的文档中阅读了有关自定义类型检查器的内容。为了后代,我将在这里重现它:
// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
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
如您所见,所有 props 都作为第一个参数传递,被测试的 props 的名称作为第二个参数传递。最后三个参数用于打印有用的错误消息并且是可选的:componentName不言自明。location将是
'prop', 'context', or 之一'childContext'(我们只对 感兴趣
'prop'),并且propFullName当我们处理嵌套 props 时,例如 someObj.someKey.
有了这些知识,我们现在可以直接调用类型检查器:
PropTypes.number({ myProp: 'bad' }, 'myProp');
// => [Error: Invalid undefined `myProp` of type `string` supplied
// to `<<anonymous>>`, expected `number`.]
看?没有所有参数就没有那么有用了。这个更好:
PropTypes.number({ myProp: 'bad' }, 'myProp', 'MyComponent', 'prop')
// => [Error: Invalid prop `myProp` of type `string` supplied
// to `MyComponent`, expected `number`.]
数组类型检查器
文档没有提到的一件事是,当您向 提供自定义类型检查器时PropTypes.arrayOf,将为每个数组元素调用它,前两个参数将分别是数组本身和当前元素的索引。现在我们可以开始勾画我们的类型检查器:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
console.log(propFullName, obj);
// 1. Check if `obj` is an Object using `PropTypes.object`
// 2. Check if all of its keys conform to some specified format
// 3. Check if all of its values are numbers
return null;
}
到目前为止,它总是会返回null(表示有效的props),但我们输入了 aconsole.log来看看发生了什么。现在我们可以这样测试:
var typeChecker = PropTypes.arrayOf(validArrayItem);
var myArray = [ { foo: 1 }, { bar: 'qux' } ];
var props = { myProp: myArray };
typeChecker(props, 'myProp', 'MyComponent', 'prop');
// -> myProp[0] { foo: 1 }
// myProp[1] { bar: 'qux' }
// => null
正如你所看到的,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;
// Check if `obj` is an Object using `PropTypes.object`
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');
// => [Error: Invalid prop `myProp[1]` of type `string` supplied to
// `MyComponent`, expected `object`.]
完美的!下一个...
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 the object is empty, consider it valid
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');
// -> myProp.Foo Foo
// myProp.bar bar
// => [Error: Invalid key `myProp.bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
伟大的!让我们将它集成到我们的validArrayItem类型检查器中:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
// Check if `obj` is an Object using `PropTypes.object`
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
// Check if all of its keys conform to some specified format
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');
// -> myProp[0].Foo Foo
// myProp[1].bar bar
// => [Error: Invalid key `myProp[1].bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
最后...
3.检查它的所有值是否都是数字
令人高兴的是,我们不需要在这里做太多工作,因为我们可以使用内置的PropTypes.objectOf:
// Check if all of its values are numbers
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;
// Check if `obj` is an Object using `PropTypes.object`
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
// Check if all of its keys conform to some specified format
var validObjectKeysError = validObjectKeys(props, propFullName, componentName);
if (validObjectKeysError) { return validObjectKeysError; }
// Check if all of its values are numbers
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 } ]);
// => null
test([ { Foo: 1 }, { bar: 2 } ]);
// => [Error: Invalid key `testProp[1].bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
test([ { Foo: 1 }, { Bar: false } ]);
// => [Error: Invalid prop `testProp[1].Bar` of type `boolean` supplied to
// `MyComponent`, expected `number`.]
有用!现在你可以像内置类型检查器一样在你的 React 组件中使用它:
MyComponent.propTypes = {
someArray: PropTypes.arrayOf(validArrayItem);
};
当然,我建议给它一个更有意义的名字并将它移到它自己的module中。