React.propTypes

1,437 阅读5分钟

最直接的定义: React为了明确表示类型错误所在位置而提供的一项类型检查功能。

propTypes(包括之后会提到的defaultProps)在我们开发过程中并不是必要的,但是对于我们成为一个更好的开发者(也可以帮我们在项目中快速定位到错误所在),学习它就很有必要了。

在这里感谢@会煮咖啡的猫,他写的那篇文章——React类型检查很详细的介绍了这点。

首先我们先来了解一下日常开发中常见的一些错误类型吧。

在rollbar上面有一篇博客,介绍了在上千个文章中重复出现的错误的种类,包括

  1. Uncaught TypeError: cannnot read property '*' of null
  2. TypeError: 'undefined' is not an object
  3. TypeError: 'null' is not an object
  4. Script Error
  5. TypeError : object doesn't support property
  6. TypeError: 'undefined' is not a function
  7. Uncaught RangeError
  8. TypeError : can not read property 'length'
  9. Uncaught Error: Cannot set property
  10. Reference Error: event is not defined

在这里我们找几个我们日常开发比较常见的说明一下。

  • Uncaught TypeErrpr: cannot read property of null

常见于查询定义未赋值的变量上的属性值。

下文中,我们在初次渲染的时候试图遍历state里面items的每一项。

class Quiz extends Component {
  componentWillMount() {
    axios.get('/thedata').then(res => {
      this.setState({items: res.data});
    });
  }

  render() {
    return (
      <ul>
        {this.state.items.map(item =>
          <li key={item.id}>{item.name}</li>
        )}
      </ul>
    );
  }
}

聪明的你一定看出来问题所在了——state未初始化,所以items默认是一个undefined,对其进行遍历自然会报错了——此外,在ComponentWillMount里面请求也是不可取的,因为此时组件尚未加载完成,在这里虽然请求到了数据 但是setState不会触发重渲染,也就得不出我们想要的页面的效果了。

  • TypeError: 'undefined' || 'null' is not an object

经过一番不严谨的测试,似乎只有在safari内核的浏览器中才会有这两个错误,edge && chrome(新版)一般直接就报 cannnot read property 这些。

(不得不说外国人真的习惯用这个浏览器了,不仅如此,国内谷歌同样冠绝群雄(但是谷歌的占内存是真的恐怖,感觉比大型游戏都能吃:在这里推荐一下edge浏览器——不得不说,微软终于醒了,做了点好东西——这个浏览器个人感觉可以直接从chrome迁移过来,体验类似但是内存松了一口气))

  • script error

这个一般多发于js尝试使用跨域等功能而发生了部分未知的错误导致的。例如你试图在CDN上面托管你的JS代码,任何报错都会只返回一个script error:出现这个现象的原因是浏览器的安全保护机制防止跨域传输数据。

  • TypeError: Object doesn't support property

本错误一般见于IE的骨灰级浏览器,现在推崇的Edge已经不会有这个报错了(取而代之的是Uncaught TypeError: this.***() is not a function)

  • uncaught RangeError

最常见的就是超内存了。

类似这种,无限重复调用,就会触发。

此外当函数的参数值超出预期范围时也会报错。

之后的都是比较容易理解的,这里不再赘叙。

propTypes

为了快速标示以上错误中类型错误所在处,我们推荐在react中使用propTypes中给传入的props设定值类型。

import propTypes from 'prop-types';

随便创建一个类:

export default class Random extends React.PureComponent{
    constructor(props){
        super(props);
        this.state = {};
    }

    render(){
        return (
            <div>{this.props.name}, how old are you?</div>
        )
    }
}

我们可以在类内外指定propTypes:

Random.propTypes = {
    name: PropTypes.string,
}
// 或者在类里面定义

export default class Random extends React.PureComponent{
    static propTypes = {
        name: PropTypes.string
    }
}

如果传的值不是string类型 则会报错

PropTypes类型:

static propTypes = {
    supposedArray: PropTypes.array, // 数组
    supposedBoolean: PropTypes.bool, // 布尔值
    supposedFunction: PropTypes.func, //函数
    supposedNumber: PropTypes.number, // 数值
    supposedObject: PropTypes.object, // 对象
    supposedString: PropTypes.string, // 字符串
    supposedSymbol: PropTypes.symbol, // symbol值

    // 放松了部分限制,numbers,string, elements,或者包含这些类型的数组(对象等仍旧会返回错误)
    supposedNode: PropTypes.node, 
    // react元素
    supposedElement: PropTypes.element,
    // 使用JS的instanceof运算符,设定prop是类的一个实例
    supposedClass: PropTypes.instanceOf(aClass),
    // 声明prop是特定的值||特定的范围中的一个
    supposedValue: PropTypes.oneOf(['a','b']),
    // 声明prop是多个类型中的一种
    supposedType: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number,
        PropTypes.instanceOf(aClass)
    ])
    // 一个由某种类型的值构成的数组
    supposedNumberArray: PropTypes.arrayOf(PropTypes.number),
    // 一个属性值是某种类型的对象
    supposedSetValueObject: PropTypes.objectOf(PropTypes.string),
    // 一个设定了部分属性的对象(只要这个对象的存在该属性且值类型符合即可)
    supposedSetTypeObject: PropTypes.shapeOf({
        name: PropTypes.string,
    })
    // 一个设定了所有属性的对象(若存在其他属性会返回一个warning)
    supposedExactObject: PropTypes.exact({
        name: PropTypes.string,
    })
    //任何数据类型的值
    supposedAny: PropTypes.any.isRequired, // 如果不传值会返回一个warning(如果不想这样 可以去掉required)
    // 此外 之前上述的所有值都可以在后面增加isRequired

}

此外,还有自定义的验证器。例如: (以下文档出自React官方文档)

  // 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
  // 它应该在验证失败时返回一个 Error 对象。
  // 验证器将验证数组或对象中的每个值。验证器的前两个参数
  // 第一个是数组或对象本身
  // 第二个是他们当前的键。
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })

Extra: children: PropTypes.element.isRequired 可以限制元素仅有一个子元素。

defaultProps

设置默认props(有点像是设置默认值的感觉)

static defaultProps = {
    name: 'myName'
}

defaultProps 用于确保 this.props.name 在父组件没有指定其值时,有一个默认值。propTypes 类型检查发生在 defaultProps 赋值后,所以类型检查也适用于 defaultProps

以上。

参考文档:

  1. Top 10 JavaScript errors from 1000+ projects (and how to avoid them)
  2. 使用PropTypes进行类型检查