Nice programing

동적 키로 객체의 PropTypes 검사

nicepro 2021. 1. 5. 21:12
반응형

동적 키로 객체의 PropTypes 검사


React는 PropTypes를 사용하여 prop의 값을 확인하는 많은 방법을 가지고 있습니다. 내가 일반적으로 사용하는 것은 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
}))
...

다시 말하지만, 적어도 각 키의 값이 숫자인지 확인하고 싶습니다. 이상적으로는 키 자체가 올바른 형식의 문자열인지 확인할 수 있기를 바랍니다.


흥미로운 질문입니다. 귀하의 질문에 따르면 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무슨 일이 일어나고 있는지 엿보기 위해 a를 던졌습니다 . 이제 다음과 같이 테스트 할 수 있습니다.

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

Of course, I would recommend giving it a more meaningful name and moving it into its own module.


To validate only the values, you can use React.PropTypes.objectOf.

...
someArray: React.PropTypes.arrayOf(
  React.PropTypes.objectOf(React.PropTypes.number)
)
...

The Problem for me was ...

  • Object has dynamic keys (which i don't wanna check)
  • Values have a fixed data structure (which i wanna check)
  • Shape of the underlying data structure is known and can be checked

    // An object with property values of a certain shape
    optionalObject: PropTypes.objectOf(
      PropTypes.shape({
        color: PropTypes.string.isRequired,
        fontSize: PropTypes.number
      })
    );
    

So for my problem the missing part was PropTypes.objectOf from there you are able to create any type of structures with dynamic keys. Also in combination with PropTypes.oneOfType it becomes very flexible.


You can create your own validator by passing a function.

See customProp here.

I believe you can do something like React.PropTypes.arrayOf(customValidator).

Here's the validator you're looking for:

function objectWithNumericKeys(obj) {
  if (Object.keys(obj).some(key => isNaN(key)))
     return new Error('Validation failed!');
}

For those that are using Immutable.js and react-immutable-proptypes library, you can use .mapOf method.

someArray: ImmutablePropTypes.listOf(ImmutablePropTypes.mapOf({
  React.PropTypes.number, // validates values
  React.PropTypes.string, // validates keys
}))

ReferenceURL : https://stackoverflow.com/questions/34362898/proptypes-check-of-object-with-dynamic-keys

반응형