reactjs-使用动态键的对象的PropTypes检查

React有很多使用PropTypes来检查道具价值的方法。 我通常使用的一个是React.PropTypes.shape({...})。但是,最近我遇到了一种情况,其中我有一个对象,该对象中将包含动态键/值。 我知道每个键都应该是一个字符串(采用已知格式),每个值都应该是一个整数。 即使使用自定义道具验证功能,它仍然假设您知道道具的钥匙。 如何使用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
}))
...

再说一遍:我至少要检查每个键的值是一个数字。 理想情况下,我还希望能够检查密钥本身是否为格式正确的字符串。

Matthew Herbst asked 2019-11-03T23:55:23Z
5个解决方案
75 votes

要仅验证值,可以使用React.PropTypes.objectOf

...
someArray: React.PropTypes.arrayOf(
  React.PropTypes.objectOf(React.PropTypes.number)
)
...
Daniel Garcia answered 2019-11-03T23:59:12Z
48 votes

这是一个有趣的问题。 从您的问题看来,您已经在支持验证文档中阅读有关自定义类型检查器的信息。为了后代,我将在此处复制:

// 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.objectOf,对吧? 它会如果我们可以做PropTypes.object并得到适当的错误,但不幸的是它涉及更多比起那个来说。 首先是要了解...

类型检查器如何工作

这是类型检查器的功能签名:

function(props, propName, componentName, location, propFullName) => null | Error

如您所见,所有道具都作为第一个参数传递,被测道具的名称作为第二个被传递。 最后三个参数用于打印出有用的错误消息,分别是可选:PropTypes.objectOf是不言自明的。 PropTypes.object将是以下之一myProp[1]'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.objectOf,将为每个数组调用元素,前两个参数将是数组本身,当前元素的索引。 现在我们可以开始草绘了我们的类型检查器:

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;
}

到目前为止,它将始终返回PropTypes.objectOf(表示有效道具),但是我们扔了PropTypes.object来看看发生了什么。 现在我们可以像这样测试它:

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

如您所见,第一项是PropTypes.objectOfPropTypes.objectmyProp[1]第二。

现在让我们充实函数的三个部分。

1.使用PropTypes.object检查PropTypes.objectOf是否是对象

这是最简单的部分:

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]/.]

大! 让我们将其集成到我们的PropTypes.objectOf类型检查器中:

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);
};

当然,我建议给它起一个更有意义的名称,将其放入自己的模块中。

Jordan Running answered 2019-11-03T23:58:47Z
12 votes

我的问题是...

  • 对象具有动态键(我不想检查)
  • 值具有固定的数据结构(我想检查)
  • 基础数据结构的形状是已知的,可以检查

    PropTypes.objectOf

因此,对于我的问题,缺少的部分是PropTypes.objectOf,您可以从那里创建带有动态键的任何类型的结构。同时结合PropTypes.oneOfType,它变得非常灵活。

teberl answered 2019-11-04T00:00:11Z
2 votes

您可以通过传递函数来创建自己的验证器。

请参阅此处的customProp

我相信您可以做类似React.PropTypes.arrayOf(customValidator)的操作。

这是您要查找的验证器:

function objectWithNumericKeys(obj) {
  if (Object.keys(obj).some(key => isNaN(key)))
     return new Error('Validation failed!');
}
Louay Alakkad answered 2019-11-04T00:00:56Z
0 votes

对于使用Immutable.js和react-immutable-proptypes库的用户,可以使用.mapOf方法。

someArray: ImmutablePropTypes.listOf(ImmutablePropTypes.mapOf({
  React.PropTypes.number, // validates values
  React.PropTypes.string, // validates keys
}))
Doug answered 2019-11-04T00:01:23Z
translate from https://stackoverflow.com:/questions/34362898/proptypes-check-of-object-with-dynamic-keys