React有很多方法可以使用PropTypes来检查道具的价值。我经常使用的是React.PropTypes.shape({...})
。但是,我最近遇到了一种情况,我有一个内部有动态键/值的对象。我知道每个键应该是一个字符串(以已知格式),每个值应该是一个int。即使使用自定义道具验证功能,它仍然假设您知道道具的关键。如何使用PropTypes检查对象/形状的键和值是否正确?
...
someArray: React.PropTypes.arrayOf(React.PropTypes.shape({
// How to specify a dynamic string key? Keys are a date/datetime string
<dynamicStringKey>: React.PropTypes.number
}))
...
再次:我想至少检查每个键的值是否为数字。理想情况下,我还希望能够检查密钥本身是否是正确格式的字符串。
答案 0 :(得分:70)
要仅验证值,您可以使用React.PropTypes.objectOf
。
...
someArray: React.PropTypes.arrayOf(
React.PropTypes.objectOf(React.PropTypes.number)
)
...
答案 1 :(得分:39)
这是一个有趣的问题。从你的问题来看,这听起来像你 在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
如您所见,所有道具都作为第一个参数传递
被测试道具的名称作为第二个传递。最后
三个参数用于打印出有用的错误消息
可选:componentName
不言自明。 location
将是其中之一
'prop'
,'context'
或'childContext'
(我们只对其感兴趣
'prop'
)和propFullName
适用于我们处理嵌套的时间
道具,例如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
(表示有效的道具),但我们
扔了console.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]
为第二个。
现在让我们充实这个功能的三个部分。
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`.]
完美!下一步...
在你的问题中,你说&#34;每个键应该是一个字符串,&#34;但是所有的对象 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]/.]
最后......
令人高兴的是,我们不需要在这里做太多工作,因为我们可以使用
内置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);
};
当然,我建议给它一个更有意义的名字并移动 它进入了自己的模块。
答案 2 :(得分:6)
对我来说问题是...
底层数据结构的形状是已知的并且可以检查
// An object with property values of a certain shape
optionalObject: PropTypes.objectOf(
PropTypes.shape({
color: PropTypes.string.isRequired,
fontSize: PropTypes.number
})
);
因此,对于我的问题,缺少的部分是PropTypes.objectOf
,您可以从那里创建带有动态键的任何类型的结构。
结合PropTypes.oneOfType
,它也变得非常灵活。
答案 3 :(得分:2)
您可以通过传递函数来创建自己的验证器。
请参阅customProp
here。
我相信你可以做React.PropTypes.arrayOf(customValidator)
之类的事情。
以下是您正在寻找的验证器:
function objectWithNumericKeys(obj) {
if (Object.keys(obj).some(key => isNaN(key)))
return new Error('Validation failed!');
}
答案 4 :(得分:0)
对于使用Immutable.js和react-immutable-proptypes库的用户,可以使用.mapOf
方法。
someArray: ImmutablePropTypes.listOf(ImmutablePropTypes.mapOf({
React.PropTypes.number, // validates values
React.PropTypes.string, // validates keys
}))