iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
8
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 11

Day10 | Props 太多, Component 就容易出錯, 就讓 Prop-Types 替你把關吧!

前言

不曉得大家還記不記得這個畫面:

對的,這是在 Day06 中埋下的伏筆,因為之後的 Hooks 就會開始運用 Props!所以趁現在來解決他吧!


前置準備

  1. 文中的專案會以 Day06 的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 一顆擁有學習熱忱的心。

使用方法

確認使用情境

怕幾天沒提到,不小心忘了 Props,就先來複習一下吧!

Props 通常會用在 Component 間的溝通,父層 Component 將某些資料或事件交給子層 Component,

讓 Component 依照傳入的 Props 值 Render 成不同的樣子

以下舉個簡單的例子:

在使用 Hello 的時候,只要將 name 藉由 Props 傳入,Hello 在 Render 時,就可以從 Props 中取出 name,不同的 name 傳入,就可以讓 Hello 呈現不同的樣子,這也是 React 中 Component 能重用的精髓。

但也許在使用時,我們會忘了需要傳 Props,而導致 Hello 無法 Render 出任何畫面,或是傳錯類型了,Hello 需要的是一個 String,但使用時卻傳入 Object 或 Array 導致畫面呈現的資料錯誤。

更嚴重的情況是,當資料是從資料庫過來,而等待 Response 回來前,儲存資料的變數都會是 undefined,這時候如果又是以 map 去處理,整個網頁就會爆炸。

但是要為每個 Props 的值都寫下判斷實在是很麻煩,因此 prop-types 出現了。

安裝 prop-types

npm install --save prop-types

設置 prop-types

安裝後就能夠打開專案中的 src/index.jsx:

會發現原本的警告消失了!其實它在 Day06 的最後就不再出現警告了,不曉得大家有沒有發現,其實這和 Component 內的結構有關,讓我們再看看前言那張擁有警告的圖:

看出差異了嗎?

只要 Props 在 {} 中使用,也就是在 HTML 的標籤中使用了 JavaScript,那就得為該 Props 加上類型驗證,

eslint 的檢測規則是這麼一回事,但是筆者會推薦,如果情況允許,就將所有的 Props 都加上 prop-types 的規則,除了減少開發後的維護成本,

也能讓接手的人更明白該賦予 Component 什麼類型的 Props,或是遺漏時 Props 的預設值會是什麼。

為了在解說時更清楚 prop-types 的作用,會以出現警告的版本,也就是將 names.map 移動到 div 內來說明,請大家當作練習,重新調整一下程式碼:

使用方法

首先在上方為 prop-typesimport

import PropTypes from 'prop-types';

接著使用 PropTypes 為每個 props 都定義型別,在 SayHello 中只接收了 names ,因此對它定義型別為陣列,且型別的內容為字串 string

SayHello.propTypes = {
  names: PropTypes.arrayOf(PropTypes.string),
};

但這麼做 ESLint 還不滿足,因為

即使我們定義了型別,也無法避免忘了給特定 Props,導致 Component 無法 Render 的問題,

所以得在替他設定一個,就算使用時忽略了某個 Props,Component 也能安心 Render 的值,以 names 來說的話,就是空陣列:

SayHello.defaultProps = {
  names: [],
};

這麼一來,就算使用 SayHello 時沒有賦予 names,就不會發生錯誤,也不會 Render 讓使用者或開發人員疑惑的畫面,但是如果你期望在忽略 Props 時出現更明顯的提示,也可以這麼做:

SayHello.defaultProps = {
  names: ['Default string'],
};

就會在沒有給定 names 的情況下:

ReactDom.render(
  <SayHello />,
  document.getElementById('root'),
);

畫面會呈現 defaultProps 設定的值:

這時候大家在試著將剛剛設定的 propTypesdefaultProps 先註解,就會明白忽略了 Props 會如何導致錯誤:

/* 暫時註解
SayHello.propTypes = {
  names: PropTypes.arrayOf(PropTypes.string),
};

SayHello.defaultProps = {
  names: ['Default string'],
};
*/

畫面會因為 namesundefined 而無法使用 map,導致 Render 失敗:

最後將註解復原,並給定 names 為裝著數字的陣列:

ReactDom.render(
  <SayHello names={[1, 2, 3]} />,
  document.getElementById('root'),
);

雖然還是能夠正常 Render 但在瀏覽器的 console 中,就會拋出一個錯誤,告訴你目前的值為 number,但該 Props 期望型別是 string

所以出現類似的 prop type 錯誤時,就得留意一下是不是有哪些 Component 在使用時傳入不正確的 Props!

本文的原始碼內容會放置於 GitHub 上,歡迎各位參考使用。


結尾

另外定義 Prop-Types 雖然麻煩,但是當網站越來越大時,能夠直接撇除掉 Props 造成的 Bug,因為要是有問題 Prop-Types 就會在 console 中告訴你錯誤,這時候就會覺得當初 Prop-Types 設定都是值得的。

如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!


上一篇
Day09 | 掌管 Lifecycle 和一切作用的 useEffect
下一篇
Day11 | 你有傳送門,我有 useContext!
系列文
在 React 生態圈內打滾的一年 feat. TypeScript31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
matuyou0301
iT邦新手 5 級 ‧ 2021-04-15 11:14:53

有預先定義真的是個好習慣,維護成本也降低很多,至少可以確定不是傳遞Props的問題。

想順便請問一下神Q

SayHello.propTypes = {
  names: PropTypes.arrayOf(PropTypes.string),
}
SayHello.defaultProps = {
  names: ['Default String']
}

這兩段程式碼有辦法寫在一起嗎? LoL

神Q超人 iT邦研究生 5 級 ‧ 2021-04-23 22:20:23 檢舉

哈哈,一個是定義 Component 的 propTypes,一個是 defaultProps,混在一起寫應該也沒有好處 XD

原來如此,看來我還是沒分清楚它們各自的用途! 謝謝神Q大!

我要留言

立即登入留言