我正在尝试为React创建一个自定义的“元组”道具类型,但是,我遇到了一些障碍。
API看起来像这样:
import {PropTypes} from 'react';
export default class MyComponent extends React.Component {
static propTypes = {
myProp: tuple(PropTypes.number, PropTypes.string),
};
这将确保myProp
是一个2元素数组,第一个元素是数字,第二个元素是字符串。
这是我走了多远:
export function tuple(...arrayOfTypeCheckers) {
return requirable(function(props, propName, componentName) {
let value = props[propName];
if(!Array.isArray(value)) {
throw new Error(`Expected array for \`${propName}\` in \`${componentName}\``);
}
if(value.length !== arrayOfTypeCheckers.length) {
throw new Error(`\`${propName}\` must have exactly ${arrayOfTypeCheckers.length} elements in \`${componentName}\`, got ${value.length}`);
}
for(let i = 0; i < value.length; ++i) {
let checker = arrayOfTypeCheckers[i];
if(!__NEED_HELP_HERE__) {
throw new Error(`${propName}[${i}] is not of the expected type in \`${componentName}\``)
}
}
return null;
});
}
(requirable
的实施可以找到here)
但是,我无法弄清楚如何实施__NEED_HELP_HERE__
。 PropTypes.string
是带签名的另一个道具类型
function(props, propName, componentName)
你会注意到它需要一个装满道具的物品,还有一个propName。该值被访问为
var propValue = props[propName];
这意味着我无法从数组中提供自己的值。
如何调用React的PropTypes.xyz
验证器?
答案 0 :(得分:2)
我发现这个问题很有用,所以我想为任何正在寻找这个问题的人(即使它已经使用了两年)提供一个最新的答案。答案基于@mpen的答案。已更新,以与最新的prop-types
版本15.6.2
一起使用。
主要更新是将secret
(类型检查器的隐藏第6个参数)传递给内部类型检查器。这样就可以在此检查器中使用标准prop-types
类型的检查器,而不会抛出Error
。也可以在其他类型检查器中使用(shape
,arrayOf
,objectOf
,tuple
本身等等)
该实现也基于prop-types
的内部实现,并符合其API(返回Error
或null
)。
createChainableTypeChecker
的实现是从库中取出的,其中删除了一些内容(检查secret
是否正确),并添加了secret
向下传递。
这是解决方案。它使用ES6类和模块导入/导出:
function CustomPropTypeError (message) {
this.message = message;
this.stack = '';
}
CustomPropTypeError.prototype = Error.prototype;
function createChainableTypeChecker (validate) {
function checkType (isRequired, props, propName, componentName, location, propFullName, secret) {
componentName = componentName || ANONYMOUS;
propFullName = propFullName || propName;
if (props[propName] == null) {
if (isRequired) {
if (props[propName] === null) {
return new CustomPropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
}
return new CustomPropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
}
return null;
} else {
return validate(props, propName, componentName, location, propFullName, secret);
}
}
var chainedCheckType = checkType.bind(null, false);
chainedCheckType.isRequired = checkType.bind(null, true);
return chainedCheckType;
}
export function tuple (...types) {
return createChainableTypeChecker((props, propName, componentName, location, propFullName, secret) => {
const value = props[propName];
if (!location) {
location = 'prop';
}
if (!propFullName) {
propFullName = propName;
}
if (!Array.isArray(value)) {
return new CustomPropTypeError(`Invalid ${location} \`${propFullName}\` supplied to \`${componentName}\`, expected ${types.length}-element array`);
}
if (value.length !== types.length) {
return new CustomPropTypeError(`Invalid ${location} \`${propFullName}\` supplied to \`${componentName}\`, expected ${types.length}-element array, got array of length ${value.length}`);
}
for (let i = 0; i < value.length; ++i) {
const error = types[i](value, i, componentName, 'element', `${propFullName}[${i}]`, secret);
if (error) {
return error;
}
}
return null;
});
}
用法:
import React from 'react';
import PropTypes from 'prop-types';
import * as CustomPropTypes from './path/to/CustomPropTypes';
class Component extends React.Component { ... }
Component.propTypes = {
myTupleType: CustomPropTypes.tuple(
PropTypes.number.isRequired,
PropTypes.string.isRequired
).isRequired,
};
<Component myTupleType={[5, 'string']} /> // No error
<Component myTupleType={['5', 6]} /> // PropType error
注意事项:
prop-types
不鼓励创建使用包含的类型检查器的自定义类型检查器(出于某种原因)。这似乎可以在15.6.2
中使用,但是我不能保证它可以在其他版本中使用,或者在以后的版本中仍然可以使用。prop-types
进行了其他尝试以防止扩展)。答案 1 :(得分:0)
使用React的内部API太难了,他们反正告诉我们not to,所以我必须自己重新实现类型检查器。< / p>
以下是代码:
const lo = require('lodash');
function tuple(...types) {
return requirable(function(props, propName, componentName, location, propFullName) {
let value = props[propName];
if(!location) {
location = 'prop';
}
if(!propFullName) {
propFullName = propName;
}
if(!Array.isArray(value)) {
throw new Error(`Invalid ${location} \`${propFullName}\` supplied to \`${componentName}\`, expected ${types.length}-element array`);
}
if(value.length !== types.length) {
throw new Error(`Invalid ${location} \`${propFullName}\` supplied to \`${componentName}\`, expected ${types.length}-element array, got array of length ${value.length}`);
}
for(let i = 0; i < value.length; ++i) {
if(!types[i](value[i])) {
throw new Error(`Invalid ${location} ${propFullName}[${i}] supplied to \`${componentName}\`, unexpected type`)
}
}
return null;
});
}
function requirable(predicate) {
const propType = (props, propName, ...rest) => {
// don't do any validation if empty
if(props[propName] === undefined) {
return;
}
return predicate(props, propName, ...rest);
};
propType.isRequired = (props, propName, componentName, ...rest) => {
// warn if empty
if(props[propName] === undefined) {
return new Error(`Required prop \`${propName}\` was not specified in \`${componentName}\`.`);
}
return predicate(props, propName, componentName, ...rest);
};
return propType;
}
const TypeCheckers = {
TypedArray: lo.isTypedArray,
Object: lo.isObject,
PlainObject: lo.isPlainObject,
RegExp: lo.isRegExp,
String: lo.isString,
Undefined: lo.isUndefined,
Number: lo.isNumber,
Null: lo.isNull,
NativeFunction: lo.isNative,
Function: lo.isFunction,
Error: lo.isError,
FiniteNumber: lo.isFinite,
NaN: lo.isNaN,
DomElement: lo.isElement,
Date: lo.isDate,
Array: lo.isArray,
Boolean: lo.isBoolean,
Stringable: obj => obj && lo.isFunction(obj.toString),
};
&#34;类型&#34;不是实际类型,它们只是取值并返回真或假的函数。我使用lodash
中的一些辅助函数,因为这比试图将它们从React中删除要容易得多,但你可以使用任何东西。
我仍在使用内部API来获取propFullName
,但是如果他们删除了它,我会进行后备。没有完整的道具名称,错误消息看起来非常糟糕。 e.g。
警告:失败的道具类型:提供给
的道具0 [1]无效PhsaDocToolbar
的意外类型
但是propFullName
我们得到:
警告:失败道具类型:提供给
PhsaDocToolbar
的意外类型无效道具[0] [1]
只有您自己创建PropTypes.arrayOf(tuple(TypeCheckers.Number, TypeCheckers.String))
等内容才能解决问题。
答案 2 :(得分:0)
做到了,对我来说很好。
//Component
<Component state={[10, () => 20]} />
//Tupple
const tupple = (...validators) =>
PropTypes.arrayOf((_, index) => {
const currentValidators = validators.filter((v, i) => i === index);
if (currentValidators.length <= 0) return true;
const [currentValidator] = currentValidators;
return currentValidator;
});
//PropsValidation
SomeComponent.propTypes = {
state: tupple(
PropTypes.number,
PropTypes.func
),
};