【react】利用prop-types第三方庫對組件的props中的變量進行類型檢測


 

1.引言——JavaScript就是一個熊孩子
 
1.1對於JSer們來說,js是自由的,但同時又有許多讓人煩惱的地方。javascript很多時候就是這么一個熊孩子,他很多時候並不會像C和java這些“好孩子”那樣循規蹈矩。因此給我們帶來許多煩惱
 
<1>運行時候控制台報錯:uncaught error,這尤其令人惱火的是系統告訴我們有錯誤但是又不告訴我們錯誤發生在哪里。試想一下,你到一個地方旅游迷了路,一個當地的熊孩子一直笑嘻嘻地跟在你后頭告訴你:“你走錯啦!”。但是不告訴你應該怎么走,你會不會很想揍他一頓?(╬ ̄皿 ̄)
 
<2>運行時報了確定的錯誤,然而我們發現這TM完全是一條驢唇不對馬嘴的錯誤報告。甚至於去stackoverflow上尋找答案,卻發現提問的錯誤場景跟自己的根本是兩碼事。讓我們回到1中場景,假如這個熊孩子很好心地告訴了你路線,結果你走到天黑發現被熊孩子狠狠得耍了,導致你不得不在大晚上露宿街頭,你會不會比1中場景更想揍他一頓?(╬ ̄皿 ̄)
 
<3>你主觀地寫錯了了一個變量的類型,比如把字符串1寫成數字1,但是系統“很好心”地不報錯誤提示。(我們都不需要特別的進行類型聲明當然不會報告錯誤提示啦)而這卻可能就是你接下來bug的源頭。讓我們回到1,2中場景,假如這個熊孩子知道你這個外地人絕逼是走錯路了,但當你問路:“我走對路了嗎?”時候,他笑靨如花滿面春風得點點頭,讓你充滿信心充滿希望得一條路走到黑。我想你此時的心情不會比1和2中的要好(╬ ̄皿 ̄)
 
<2>中情況有時候比較難以避免
<1>中情況我們可以通過熟悉主要的6種uncaught error的情形加以判斷。(在下一篇文章里我會討論這個問題)
<3>中的情況呢,完全可以用類型檢測的方式加以避免,這也就是我這篇文章所講到的內容
 
本節主要討論的是與react配套的類型檢測庫——prop-types的運用
 
今天我在這篇文章里面介紹的內容,就是通過react的propTypes進行類型檢測,。顧名思義prop-types就是對react組件中props對象中的變量進行類型檢測的,因為props是react數據流的管道,我們通過prop-types就可以輕松監控react里大多數據的變量類型先介紹下propTypes的基本用法。
 
2.prop-types基礎入門
 
2.1首先你需要通過在終端npm install prop-types安裝一個叫prop-types的第三方包
 
2.2然后通過下面的寫法對你的某一個組件的props中的變量進行類型檢測:
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'}
              />)
            }
}
 
比如這個例子,我們通過props從父組件向子組件傳遞屬性,你原本試圖通過number,array和boolean這三個屬性分別向Son中傳遞一個數字,數組和一個布爾型數值,但由於你剛一下子追完了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'}
                        />)
                  }
}
 
然后我們就能看到報的錯誤了,而且這個時候,報的錯誤包括錯誤的props屬性名稱,錯誤的變量類型,屬性所在的組件名稱,預期的正確的變量類型,錯誤代碼的位置以及其他更詳細的信息。
 
這種“人為控制”的報錯比一般的系統報錯看起來應該要親切自然得多吧...你大可以說:這個error是我“私人定制”的呦 (//▽//)
 
propTypes 能用來檢測全部數據類型的變量,包括基本類型的的string,boolean,number,以及引用類型的object,array,function,甚至還有ES6新增的symbol類型
 
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類型
}
 
【注意】下面這些是從官方英文文檔里引用過來的,你大概能夠注意到,五種基本類型中的undefined和null並不在此列,propTypes類型檢測的缺憾之一是,對於undefined和null的值,它無法捕捉錯誤
 
讓我們把上述實例中的Father組件傳遞給Son組件修改一下,改成:
 
class Father extends React.Component{
    render(){
       return (<Son
                 number = {null}
                 array = {null}
                 boolean = {null}
                />)
             }
}
 
結果是輸出台不報任何錯誤,(當然你改成undefined也是同樣效果)。
 
 
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屬性的規定類型包括字符串和數字兩種,所以此時控制台無報錯
 
 
當然,如果你改為number = {數組或其他類型的變量},那么這時就會報錯了
 
 
3.3 通過oneOf實現多選擇檢測——可規定多個檢測通過的變量的值
 
3.2是規定了多個可檢測通過的數據類型,那么同樣的道理,我們也可以規定多個可檢測通過的變量的值,這就要用到PropTypes里的oneOf方法,和PropTypes方法一樣oneOf方法接收參數的是一個數組,數組元素是你希望檢測通過的變量的值,比如我們把上面類型檢測的部分改成:
 
Son.propTypes = {
    number:PropTypes.oneOf(
          [12,13]
      )
}

 

那么運行時就會報這樣一段錯誤:
 
 
3.4 arrayOf,objectOf實現多重嵌套檢測
 
試想一下,如果我們檢測的是基本類型的變量,那么這自然是很簡單的,但當我們要檢測的是一個引用類型的變量呢?當我們除了檢測這個變量是否符合規定的引用類型外(Object/array),還想要進一步檢測object中的屬性變量或array中數組元素的數據類型時,單靠上面的方法已經不能滿足要求了。這時候就要用到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']}/>,報錯
 
【注意】雖然報錯但是這並不會影響程序的正常運行(譬如上面我們看到渲染仍然是正常的),因為本質上說類型檢測報的是非致命性錯誤warning而不是致命性錯誤error(區別在於是否影響了正常運行)。 對objectOf也是同樣的做法
 
3.5 通過shape方法檢測目標對象不同屬性的不同數據類型
 
如果你認真思考一下的話,你會發現3.4中的objectOf有一個缺陷,就是它內部的屬性的數據類型被強行規定為一種,但通常一個對象里應該是有多種不同類型的屬性了,那么這時候objectOf就不符合要求了,我們應該使用shape方法,其用法:
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:'彭湖灣',age:20}}/>改成<Son object = {{name:'彭湖灣',age:'20'}}/>,然后就能喜聞樂見得報錯了

 

3.6 通過isRequired檢測props中某個必要的屬性(如果該屬性不存在就報錯)
 
有時候,我們在對某個變量進行類型檢測時,我們不僅要求它符合預期的類型,同時也要求它是必須寫入的,這時候就要用到isRequired。
【分析】
Son.propTypes = {
          number:PropTypes.number
}

 

這段代碼的作用是當你在props中寫入number屬性且number屬性類型錯誤時給予報錯提示,可如果你壓根就沒有寫入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
}

 

再運行,我們就又可以喜聞樂見得看到錯誤了:
 
【注意】在這里給大家提個問題:我們上述的寫法是number:PropTypes.number.isRequired,這要求number是數字類型,但如果你不想控制number的類型而僅僅是想控制它的必要性呢?難道寫成number: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)
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM