react prop-types的使用讲解

1.引言--JavaScript就是一个熊孩子

1.1对于JSer们来说,JS是自由的,但同时又有许多让人烦恼的地方.javascript 很多时候就是这么一个熊孩子,他很多时候并不会像Ç和java的这些“好孩子”那样循规蹈矩。因此给我们带来许多烦恼

<1>运行时候控制台报错:未记错误,尤其这令人恼火的的英文系统-告诉我们有错误但是又不告诉我们错误发生在哪里试想一下,你到一个地方旅游迷了路,一个当地的熊孩子一直笑嘻嘻地跟在你后头告诉你?!“你走错啦”但是不告诉你应该怎么走,你会不会很想揍他一顿(╬皿)

<2>运行时报了确定的错误,然而我们发现这TM完全是一条驴唇不对马嘴的错误报告。甚至于去计算器上寻找答案,却发现提问的错误场景跟自己的根本是两码事。让我们回到1中场景,假如这个熊孩子很好心地告诉了你路线,结果你走到天黑发现被熊孩子狠狠得耍了,导致你不得不在大晚上露宿街头,你会不会比1中场景更想揍他一顿?(╬皿¯)

<3>你主观地写错了了一个变量的类型,比如把字符串1写成数字1,但是系统“很好心”地不报错误提示。(我们都不需要特别的进行类型声明当然不会报告错误提示啦)而这却可能就是你接下来错误的源头让我们回到1,2中场景。假如这个熊孩子知道你这个外地人绝逼是走错路了,但当你问路:“我走对路了吗?”时候,他笑靥如花满面春风得点点头,让你充满信心充满希望得一条路走到黑。我想你此时的心情不会比1和2中的要好(╬皿¯)

<2>中情况有时候比较难以避免

<1>中情况我们可以通过熟悉主要的6种uncaught error的情形加以判断。(在下一篇文章里我会讨论这个问题)

<3>中的情况呢,完全可以用类型检测的方式加以避免,这也就是我这篇文章所讲到的内容

本节主要讨论的是与反应配套的类型检测库--prop类型的运用

今天我在这篇文章里面介绍的内容,就是通过反应过来的propTypes进行类型检测。顾名思义丙类型就是对反应组件中道具对象中的变量进行类型检测的,因为道具的英文反应数据流的管道,我们通过丙类型就可以轻松监控反应里大多数据的变量类型先介绍下propTypes的基本用法。

2.prop类型基础入门

2.1首先你需要通过在终端npm install prop-types安装一个叫prop-types的第三方包

2.2然后通过下面的写法对你的某一个组件的道具中的变量进行类型检测:

yourComponent.propTypes = {
    属性1:属性1的变量类型,
    属性2:属性2的变量类型
    //...
}

3.propTypes的使用全解

3.1利用propTypes检测全部数据类型的变量

import React from 'react'
  class Son extends React.Component{
  render(){
    return (<div style ={{padding:30}}>
              {this.props.number}
              <br/>
              {this.props.array}
              <br/>
              {this.props.boolean.toString()}
            </div>)
           }
}
class Father extends React.Component{
   render(){
     return (<Son
              number = {'1'}
              array = {'[1,2,3]'}
              boolean = {'true'}
              />)
            }
}

比如这个例子,我们通过道具从父组件向子组件传递属性,你原本试图通过数目,阵列和布尔这三个属性分别向子中传递一个数字,数组和一个布尔型数值,但由于你刚一下子追完了50多集“人民的名义”,导致你过度疲惫,把它们都写成了字符串,虽然渲染是正常的,但这可能会导致你接下来调用一些方法的时候发生错误,而系统并不提供任何提示。

让我们给它加上propTypes的类型检测:

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
   render(){
        return (<div style ={{padding:30}}>
                      {this.props.number}
                      <br/>
                      {this.props.array}
                      <br/>
                      {this.props.boolean.toString()}
                    </div>)
                  }
}
Son.propTypes = {
        number:PropTypes.number,
        array:PropTypes.array,
        boolean:PropTypes.bool
}
class Father extends React.Component{
    render(){
         return (<Son
                       number = {'1'}
                       array = {'[1,2,3]'}
                       boolean = {'true'}
                        />)
                  }
}

然后我们就能看到报的错误了,而且这个时候,报的错误包括错误的道具属性名称,错误的变量类型,属性所在的组件名称,预期的正确的变量类型,错误代码的位置以及其他更详细的信息。

这种“人为控制”的报错比一般的系统报错看起来应该要亲切自然得多吧...你大可以说:这个错误是我“私人定制”的呦(//▽//)

propTypes能用来检测全部数据类型的变量,包括基本类型的的字符串,布尔值,数字,以及引用类型的对象,数组,函数,甚至还有ES6新增的符号类型

Son.propTypes = {
     optionalArray: PropTypes.array,//检测数组类型
     optionalBool: PropTypes.bool,//检测布尔类型
     optionalFunc: PropTypes.func,//检测函数(Function类型)
     optionalNumber: PropTypes.number,//检测数字
     optionalObject: PropTypes.object,//检测对象
     optionalString: PropTypes.string,//检测字符串
     optionalSymbol: PropTypes.symbol,//ES6新增的symbol类型
}

【注意】下面这些是从官方英文文档里引用过来的,你大概能够注意到,五种基本类型中的不确定和空并不在此列,propTypes类型检测的缺憾之一是,对于不确定的和无效的值,它无法捕捉错误

让我们把上述实例中的父组件传递给子组件修改一下,改成:

class Father extends React.Component{
    render(){
       return (<Son
                 number = {null}
                 array = {null}
                 boolean = {null}
                />)
             }
}

结果是输出台不报任何错误,(当然你改成未定义也是同样效果)。

3.2  通过oneOfType实现多选择检测-可规定多个检测通过的数据类型

上个例子中类型检测的要求是一个变量对应一个数据类型,也就是规定的变量类型只有一个。那么怎样能让它变得灵活一些,比如规定多个可选的数据类型都为检测通过呢? PropTypes里的oneOfType方法可以做到这一点,oneOfType方法接收参数的是一个数组,数组元素是你希望检测通过的数据类型。

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
render(){
return (<div style ={{padding:30}}>
{this.props.number}
</div>)
}
}
Son.propTypes = {
number:PropTypes.oneOfType(
[PropTypes.string,PropTypes.number]
)
}
class Father extends React.Component{
render(){
//分别渲染数字的11和字符串的11
return (<div>
<Son number = {'字符串11'}/>
<Son number = {11}/>
</div>)
}
}

这时候,因为在类型检测中,号码属性的规定类型包括字符串和数字两种,所以此时控制台无报错

当然,如果你改为number = {数组或其他类型的变量},那么这时就会报错了

3.3  通过oneOf实现多选择检测-可规定多个检测通过的变量的值

3.2是规定了多个可检测通过的数据类型,那么同样的道理,我们也可以规定多个可检测通过的变量的值,这就要用到PropTypes里的oneOf方法,和PropTypes方法一样oneOf方法接收参数的是一个数组,数组元素是你希望检测通过的变量的值,比如我们把上面类型检测的部分改成:

Son.propTypes = {
    number:PropTypes.oneOf(
          [12,13]
      )
}

那么运行时就会报这样一段错误:

3.4 arrayOf,objectOf实现多重嵌套检测

试想一下,如果我们检测的是基本类型的变量,那么这自然是很简单的,但当我们要检测的是一个引用类型的变量呢?当我们除了检测这个变量是否符合规定的引用类型外(对象/阵列),还想要进一步检测对象中的属性变量或阵列中数组元素的数据类型时,单靠上面的方法已经不能满足要求了。这时候就要用到PropTypes的arrayOf,objectOf方法。

arrayOf接收一个参数,这个参数是规定的数组元素的数据类型.objectOf接收的参数则是属性的数据类型

我们对上述例子做些修改:

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
    render(){
       return (<div style ={{padding:30}}>
                {this.props.array}
               </div>)
           }
}
Son.propTypes = {
     array:PropTypes.arrayOf(PropTypes.number)
}
class Father extends React.Component{
    render(){
       return (<div>
                 <Son array = {[1,2,3,4]}/>
               </div>)
}
}

正常渲染,然后我们把<Son array = {[1,2,3,4]} />改为<Son array = {['1','2','3','4']} /> ,报错

【注意】虽然报错但是这并不会影响程序的正常运行(譬如上面我们看到渲染仍然是正常的),因为本质上说类型检测报的是非致命性错误的警告而不是致命性错误的错误(区别在于是否影响了正常运行)。对objectOf也是同样的做法

3.5  通过形状方法检测目标对象不同属性的不同数据类型

如果你认真思考一下的话,你会发现3.4中的objectOf有一个缺陷,就是它内部的属性的数据类型被强行规定为一种,但通常一个对象里应该是有多种不同类型的属性了,那么这时候objectOf就不符合要求了,我们应该使用形状方法,其用法:

PropTypes.shape({
   属性1:类型1,
   属性2:类型2,
  //...
}),

举个例子:

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
     render(){
        return (<div style ={{padding:30}}>
                  {'我的名字叫' + this.props.object.name}
                  <br/>
                  {'我的年龄是' + this.props.object.age}
                 </div>)
             }
}
Son.propTypes = {
     object:PropTypes.shape({
     name:PropTypes.string,
     age:PropTypes.number
      })
}
class Father extends React.Component{
    render(){
       return (<div>
                  <Son object = {{name:'彭湖湾',age:20}}/>
               </div>)
     }
}

无报错,把<Son object = {{name:'彭湖湾',年龄:20}} />改成<Son object = {{name:'彭湖湾',年龄:'20'}} /> ,然后就能喜闻乐见得报错了

3.6通过isRequired检测道具中某个必要的属性(如果该属性不存在就报错)

有时候,我们在对某个变量进行类型检测时,我们不仅要求它符合预期的类型,同时也要求它是必须写入的,这时候就要用到isRequired。

【分析】

Son.propTypes = {
          number:PropTypes.number
}

这段代码的作用是当你在道具中写入号码属性且号码属性类型错误时给予报错提示,可如果你压根就没有写入号码属性呢?没错,什么错误都不会报。这就是使用isRequired的必要性

【栗子】

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
    render(){
       return (<div style ={{padding:30}}>
                  {this.props.number}
               </div>)
           }
}
Son.propTypes = {
     number:PropTypes.number
}
class Father extends React.Component{
   render(){
      return (<div>
                <Son />
              </div>)
        }
}

控制台无任何输出

如果我们改成:

Son.propTypes = {
    number:PropTypes.number.isRequired
}

再运行,我们就又可以喜闻乐见得看到错误了:

【注意】在这里给大家提个问题:我们上述的写法是数量:PropTypes.number.isRequired,这要求数是数字类型,但如果你不想控制数的类型而仅仅是想控制它的必要性呢?难道写成号:isRequired或number:PropTypes.isRequired? 这个时候PropTypes.any就登场啦!它代表了该变量可取任何一种数据类型,所以你可以写成这样--number:PropTypes.any.isRequired

3.7应对更复杂的类型检测 - 将PropTypes的属性值写成函数

Son.propTypes = {
      prop:function(props,propName,componentName){
          if(/*判断条件*/){
               return new Error(/*错误的参数*/)
           }
    }
}

在属性prop的类型检测中,属性值是一个函数,在这里props是包含prop的props对象,propName是prop的属性名,componentName是props所在的组件名称,函数的返回值是一个Error对象

import React from 'react'
import PropTypes from 'prop-types';
class Son extends React.Component{
         render(){
               return (<div style ={{padding:30}}>
                        {this.props.email}
                       </div>)
                  }
}
Son.propTypes = {
     email:function(props,propName,componentName){
            if(!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(props[propName])){
                  return new Error('组件' + componentName+ '里的属性' + propName + '不符合邮箱的格式');
                         }
                }
}
class Father extends React.Component{
        render(){
             return (<div>
                        <Son email = {2314838004}/>
                     </div>)
                }
}

在这里我们利用正则表达式检测传递到Son组件的email属性是否符合邮箱格式,如果不符合就抛出错误,那么2314838004显然不符合这一要求,所以我们就得到下面的demo:(其实加上qq.com就是我的邮箱啦 哈哈)

4.ES7下类型检测的新写法:

可能你觉得把propTypes写在类外看起来有些怪怪的,在ES7的静态类属性的支持下,你可以这样写:

class Son extends React.Component{
static propTypes = {
       //..类型检测
}
render(){
   return (/* 渲染*/)
     }
}

但注意,这在ES7下生效

5.props-types的独立与react.PropTypes的弃用

在上面我是利用props-types这个独立的第三方库来进行类型检测的,但在不久前(react V15.5以前),它使用的是react内置的类型检测,而不是第三方库(也就是说我们现在的prop-types是当初以react内置的PropTypes对象为基础分离出来的)

翻译成中文就是:

所以说在你也可以这样进行类型检测,虽然并不推荐(为了保持向下兼容这在最新版本的react上仍然是可用的)

Son.propTypes = {
    number:React.PropTypes.number
}

6.参考资料:

react官方文档/高级指导/类型检测(docs/advanced guide/Typechecking with propTypes)

https://facebook.github.io/react/docs/typechecking-with-proptypes.html

转载地址:https://www.cnblogs.com/penghuwan/p/6796139.html

猜你喜欢

转载自blog.csdn.net/sjpeter/article/details/81079619