寫一個公用的組件很難,你必須細心地考慮不少問題,好比應該暴露出哪些 props
。react
本文將簡要的介紹 API 設計中的一些最佳實踐,以及編寫 React 組件的 10 條參考規則。後端
API (Application Programming Interface)是兩段代碼或者兩個應用如何交互的一個定義或者接口。數組
同理,組件定義的 props
也是 API,這是用戶與組件交互的方式。ide
因此,設計一個 API 的時候應該有哪些規則和注意事項?咱們作了不少研究並結合實踐給出了 4 條 API 設計的最佳實踐:函數
最重要的規則之一就是要保持穩定,意思就是要最大限度的減小破壞性的升級,若是你作了破壞性的升級,必定要寫一個完整的升級指南,若是可能的話,再提供一些 API 讓用戶消化升級的過程,使用戶升級版本的成本下降。工具
若是你要發佈 API,請使用 語義化版本,以便用戶能夠自由的選擇他們須要的版本。post
調用 API 出錯的時候,返回給客戶端一個詳細的錯誤說明,而且告訴客戶端應該如何解決。在沒有任何上下文的狀況下返回一個 「調用出錯」 的錯誤信息給客戶端並非一個友好的體驗。學習
開發人員都是很傲嬌的,並不想在使用你 API 的時候感到迷惑,換句話說,使你的 API 儘量的直觀,規範。能夠經過遵循一些原則和命名規範去實現。優化
舉個例子,你的 API 提供了 boolean 類型的參數,參數命名的時候你在一個地方用 is 作前綴,另一個地方又用了 has 作前綴,而後其餘地方又用了另一個前綴,這會讓開發人員比較迷惑。
固然不是說功能多了很差,只是要善用外觀模式或者命令模式等去封裝一些操做,作到高內聚,API 過多會增長學習成本,一個高內聚的 API 會被認作是一個易於使用的 API。
上面 4 條規則在 REST APIs 中應用的很好,以前提過的,咱們的組件也有它的 API,就是 props
,咱們該如何定義 props
使它不違反上面的規則呢?下面列出 10 條建議:
若是你沒有寫個組件的使用文檔,好吧,使用者能夠看你的代碼,但這並非一個好的體驗。
有不少寫文檔的工具,這裏推薦三個:
無論選用哪一個,必定要把全部的 API 使用說明都寫出來。
HTML是一種以語義方式結構化信息的語言。然而,咱們的大多數組件都是由 <div />
標籤組成的。它在某種程度上是沒問題的,由於通用組件不知道咱們想要的是 <article />
或 <section />
仍是 <aside />
,可是這樣並不優雅。因此,咱們建議容許組件接受一個 prop
,它將覆蓋正在呈現的 DOM 元素。如下是如何實現它的示例:
function Grid({ as: Element, ...props }) {
return <Element className="grid" {...props} /> } Grid.defaultProps = { as: 'div', }; 複製代碼
在 jsx 中把 as
prop 重命名文本地變量 Element
,而且把默認值設爲 div
。
使用 <Grid />
的時候傳遞你想要的標籤名就行了。
function App() {
return (
<Grid as="main"> <MoreContent /> </Grid>
);
}
複製代碼
這樣在 React 中使用是沒有問題的,另一個經典的例子就是你有一個 <Button />
組件,想把它渲染成 React Router 的 <Link />
<Button as={Link} to="/profile">
Go to Profile
</Button>
複製代碼
Boolean props 聽起來是個不錯的選擇,由於你能夠不用指定值,看起來很是優雅。
<Button large>BUY NOW!</Button>
複製代碼
儘管 Boolean props 看起來很是優雅,可是它只能支持兩個值 true
or false
若是你的組件設計了不少種 boolean 類型的 props 看起來就比較累贅了:
<Button large small primary disabled secondary>
WHAT AM I??
</Button>
複製代碼
換句話說,boolean 一般很難根據需求去擴展。換成字符串枚舉是一個更好的選擇,它能夠擴展爲二元值以外的任意值。
<Button variant="primary" size="large">
I am primarily a large button
</Button>
複製代碼
不是說 boolean props 一無可取,一些不可能擴展而且只有兩個值的 prop 好比 disabled 仍是用 boolean 類型。
React 有幾個特殊的 prop,好比 key
,他們有特殊的處理機制,還有一個就是 children
。
在開始標籤和結束標籤中間的內容都會被塞進 props.children
props,應該儘量多的使用它,由於它比一個 content
prop,或者一些文本內容須要傳遞的時候更易使用。
<TableCell content="Some text" />
// vs
<TableCell>Some text</TableCell>
複製代碼
使用 props.children
有幾個好處。首先,它相似於常規 HTML 的使用方式。其次,你能夠自由地傳遞任何你想要的東西,而不是將 leftIcon
和 rightIcon
prop 添加到組件中,只需將它們做爲 props.children
prop 的一部分傳遞:
<TableCell>
<ImportantIcon /> Some text </TableCell>
複製代碼
你可能會說你的組件只會渲染純文本,不須要其餘東西,如今看來可能沒問題,可是之後需求不斷變化的時候你就會發現 props.children
的好處。
有時候咱們會寫一些內部邏輯很複雜的組件,好比 AutoComplete
或者一些圖表。
這些類型的組件要渲染的內容一般依賴外部的 API,隨着時間的推移,可能還要實現一些特殊的需求
咱們如何提供一個單一又標準化的 prop 讓調用者去控制或者覆蓋組件內部的默認邏輯呢?
解決方案是 state reducers
模式,這裏有一篇文章 post about the concept itself
總結下來,state reducer
模式就是讓消費者能夠訪問到組件內部發生的一切事件、狀態,從而進行更高級的自定義配置
function MyCustomDropdown(props) {
const stateReducer = (state, action) => {
if (action.type === Dropdown.actions.CLOSE) {
buttonRef.current.focus();
}
};
return (
<>
<Dropdown stateReducer={stateReducer} {...props} />
<Button ref={buttonRef}>Open</Button>
</>
}
複製代碼
當你建立一個新組件的時候,確保剩餘的 props 被傳遞到能夠生效的 element 上,沒必要僅僅是爲了向底層組件傳遞 props 就額外添加一個。能夠用 ...rest
操做符:
function ToolTip({ isVisible, ...rest }) {
return isVisible ? <span role="tooltip" {...rest} /> : null; } 複製代碼
儘可能爲 props 提供默認值,這樣也能夠大大減小須要傳遞 props 的數量,以 onClick
爲例,能夠提供一個空函數做爲默認值,另外能夠爲字符類型的 prop 提供一個空字符串做爲默認值,這樣當沒有傳遞 prop 的時候就能夠確保處理的是空字符串而不是 undefined 或者 null 等不肯定的值
HTML 標籤自己就含有本身的一些屬性,就是他本身的 API,爲何不用呢?
就像前面提到過的,減小暴露的 API,爲何要添加一個 screenReaderLabel prop 而不用自身原有的 aria-label API呢?
因此,請不要爲了「易用性」而去重複定義,好比添加了 screenReaderLabel prop,而後又傳遞了一個 aria-label 屬性,那麼最終顯示應該是什麼呢?
另外,請不要覆蓋 HTML 標籤自己的屬性,好比 <button />
元素的 type 屬性,能夠是 submit(默認)button 或者 reset,然而許多開發者都將它從新定義爲其餘的含義的props(primary warning info 等),這樣就令使用者比較迷惑。
代碼既文檔,如今已經有 prop-types
包可用,去使用它。
若是沒有傳遞一些必須的 props,控制檯會報錯,若是用的是 TypeScript 或者 Flow,那開發體驗就更好了。
最後,遵循最重要的規則。確保您的 API 和 「組件體驗」 針對將使用它的人員或是開發同事進行了優化。
提高開發體驗的一個方法就是提供詳細的報錯信息,或者是在開發模式下在控制檯發出警告。
在控制檯報錯或者報警的時候,若是開發人員也看到了對應錯誤或者警告的文檔連接,會大大提高組件的使用體驗。
沒必要擔憂過於冗長的錯誤信息會佔用太大空間,何況構建生產環境的時候也不會把這些信息打包進去。
React 自己就是一個很是優秀的類庫,當你忘記使用 key 或者拼錯了生命週期的名字等等,都會在控制檯收到大量詳細的錯誤警告信息。