当前位置: 首页 > 面试题库 >

使用动态键对对象进行PropTypes检查

乐正烨熠
2023-03-14
问题内容

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

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


问题答案:

注意 :该答案写于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

如您所见,所有道具都作为第一个参数传递,被测试道具的名称作为第二个参数传递。最后三个参数用于打印出有用的错误消息,并且是可选的: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

如您所见,propFullNamemyProp[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);
};

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



 类似资料:
  • 我有一个类扩展了React。组件类,我将为我希望接收的道具传递我自己的接口。问题是当我使用webstorm linter说有问题,指出类型“typeof SomeComponent”上不存在属性“propTypes”。 我不知道为什么会这样...... 的代码为 当我编写以下代码创建函数组件时,会引发类似的异常: 在本例中,使用

  • 问题内容: 首先,我使用Cheerio进行一些DOM访问并使用Node.js进行解析。美好的时光。 情况如下: 我具有创建对象所需的功能。该对象为其键和值使用变量,然后返回该单个对象。例: 它输出: (返回对象fyi的数组) 我实际上需要成为的字符串。 考虑到我要做什么,在Java中将字符串分配为键的最佳方法是什么? 问题答案: 在JavaScript 的新ES2015标准(以前称为ES6)中,可

  • 我有一个静态结构的对象: 我想按键更新它的属性。例如,如果我收到 然后我想更新对象,并有: 我该怎么办?我试图创建一个新对象,类似这样: 我不知道如何从变量中设置键和值的名称

  • 问题内容: 我有以下我想用流程注释的内容: 我知道该怎么做了类型检查,这样的类型的(如上图所示),但我怎么能型检查其属性呢? 已经尝试过: 但是随后流程只是抱怨而从未使用过。 问题答案: 因此,您要发送类型为的道具,该道具必须具有属性和? 如果没有编写执行此检查的自定义函数,则无法实现。为此,您需要这样声明: 这是官方文档所说的: 您还可以指定一个自定义验证器。如果验证失败,它将返回一个Error

  • 我已经成功地将镶嵌到我的Android应用程序中,它可以读取我捕获的任何图像,但准确性非常低。但大多数时候,我在捕获后没有得到正确的文本,因为感兴趣区域周围的一些文本也会被捕获。 我想阅读的只是来自矩形区域的所有文本,准确,没有捕捉矩形的边缘。我已经做了一些研究,并在stackoverflow上发布了两次,但仍然没有得到满意的结果! 以下是我发的2个帖子: https://stackoverflo

  • 动态SQL实际上就是带参数的SQL。通过PreparedStatement对象可以执行动态的SQL。由于动态SQL没有参数名,只有参数索引,因此,PreparedStatement接口的getXxx方法和setXxx方法只能通过参数索引来确定参数。PreparedStatement对象和Statement对象的使用方法类似,所不同的是Connection对象使用prepareStatement方法