iT邦幫忙

2021 iThome 鐵人賽

DAY 1
1

參賽前言

第一次參賽是 2019鐵人賽(連結),也是剛接觸 React 不久,透過那次真的覺得收穫良多。雖然參加完有種這輩子除非瘋了,不然絕不可能參加第二次的心情,但看到後來鐵人賽得獎作品居然可以出書,所以今年終於下定決心要再來瘋一次,畢竟人可以老,但青春不能老/images/emoticon/emoticon65.gif

這次選的題目,在頭洗下去之後才覺得怎麼這個題目有點大/images/emoticon/emoticon04.gif,而且真的還蠻需要經驗才有辦法駕馭這個題目,因此在準備的過程當中也越來越覺得自己很渺小,但也因為有遇到困難,才算得上是一個挑戰,越不容易完成,在真的完成之後才越覺得珍惜和可貴。

雖然青春的熱血無限,但畢竟時間有限,希望在有限的時間當中能夠盡力而為,跟隊友們、參賽者們、讀者們一起完成這個挑戰!


內容提要

這次如同參賽題目,以 React 框架來進行實作,每篇的架構主要為:

  • 元件介紹
  • 參考設計 & 屬性分析
  • 介面設計
  • 元件實作

但會依照不同的需要做一些微調,但希望能盡量保持一致。
這次會用到處理樣式的工具是選用 styled-component,styled-component 是一個 CSS-In-JS 的函式庫,他將樣式元件化的概念能讓我們在編寫 React 元件時看起來更加簡潔,也有許多很好用的特性來幫助我們處理複雜的樣式,例如我們可以像在處理元件一樣的將 props 傳入,並且撰寫一些判斷邏輯來處理 CSS 樣式等等。

另外在參考設計的部分,由於時間有限,因此先選用知名的 Material-UI 以及 Antd 來參考為主,其他的函式庫或是參考文章,會視文章需要及時間許可的條件選擇性的加入。

為了避免太過於流水帳的撰寫,元件實作的部分只會提到一些關鍵步驟,其他部分會在文章最後附上完整 source code 以及能幫助我們 preview 的 storybook 提供給大家參考。

希望以上描述能夠幫助到讀者,那我廢話到此為止,開賽啦!!!/images/emoticon/emoticon08.gif


元件介紹

Button 元件代表一個可點擊的按鈕,在使用者點擊之後會觸發相對應的業務邏輯。
在過往的經驗當中,我們常在下面情境下會需要按鈕:

  • 填完表單之後要點擊「確認」來送出資料,或是點擊「取消」來放棄編輯
  • 複雜的表單動作,有「上一步」、「下一步」的按鈕
  • 操作網站上一些無法復原的危險動作(ex: 刪除資料),需要第二次的確認「按鈕」
  • 在需要編輯資料的系統,我們要做一些 CRUD 相關的操作
    • 點開文章以瀏覽詳情
    • 新增文章
    • 編輯文章
    • 刪除文章
    • 上傳檔案
  • 為了引導使用者前往購物、前往註冊、前往你希望他去的地方,藉此來達成一些商業目的等等,也需要明顯的按鈕來引導他下一步該做什麼

按鈕是幾乎在每一個網站上都會出現的元件,雖然如此,但是他被放置在不同地方所希望達到的目的也不盡相同,上面信手捻來就可以舉出很多例子。但是也因為同一個元件要用來做很多不同的事情,因此他的變化也很多。

雖然很常出現而被覺得是一個很簡單的元件,但沒有仔細考慮的話,很多細節會容易被忽略,很容易發生一開始設計了一個想要全站共用樣式的按鈕,但變化到後來因為一開始沒有考慮周詳,導致後來無法共用而不得不再重複造輪子的慘案發生。

參考設計 & 屬性分析

在設計元件的時候,一方面我們會擔心自己考慮得不夠周詳,另一方面也會擔心設計出來的東西過於特立獨行,自己以為這樣很好,但是其實沒有人這樣做,雖然說得上是創新,但反過來,也很容易讓使用者甚至其他開發者無法理解你的好意。

因此我通常會找一些知名的 React UI library 來參考他的樣式及介面的設計,比較常參考的有

  • Material UI (MUI)
  • Ant Design (Antd)
  • React Bootstrap

變化模式

變化模式上面的變化,Material UI 叫做 variant,Ant Design 叫做 type,Bootstrap 裡面看起來比較類似的屬性是 variant,但是 Bootstrap 的 variant 裡面同時可以控制顏色以及描邊樣式。

在變化模式下面有幾種變化

  • contained button 實心按鈕
  • outlined button 描邊按鈕
  • dashed button 虛線按鈕
  • text button 文本按鈕
  • link button 連結按鈕

MUI 上面沒有特別區分 text button 和 link button,甚至連 dashed button 也沒有。以變化模式上來看的話,Antd 似乎是比較豐富。

但其實我個人是覺得並不會因為 MUI 他在 variant 裡面沒有那麼多種變化,就表示他輸人家一籌,因為或許有些樣式並不是很常用到需要納入 variant ,畢竟要多維護一種樣式也是需要增加維護成本,而且透過 MUI 的客製化樣式的機制也能夠做到同樣的效果。以我個人開發的經驗,老實說我真的也不是很常遇到需要 dashed button 的時候,所以我覺得不能用這樣「我有你沒有」的標準來斷定一個 library 的優劣,畢竟不同元件庫有他不同的使用情境。反倒是我覺得這個元件庫方不方便我們客製化,才是我比較在意的。

所以到底要不要把每一種變化都納入自己的設計考量,我覺得也倒不一定,需要考慮自己網站的需求,或是跟團隊、設計師一起來討論。不過因為我們知道有這些變化的可能性之後,也能避免把 code 寫死,導致以後如果哪天哪根筋不對,突然又需要的時候,可以不需要大改動就能夠擴充。

顏色屬性

MUI 的顏色屬性叫做 color ,可以傳入的參數有

  • primary
  • secondary
  • default
  • inherit

Antd 看起來似乎沒有特別的 color 屬性,他預設是 primary 藍色,若 danger 屬性為 true 就會變紅色。

Bootstrap 是透過 variant 屬性來決定,有

  • primary
  • secondary
  • success
  • warning
  • danger
  • info
  • light
  • dark
  • link

當我們在做後台系統或是不需要那麼繽紛的 B2B 系統的時候,在顏色上面有 primary, secondary 規範是很好的,全站的主題會統一,顏色要告訴使用者的訊息也很清楚,例如 danger 的顏色或 warning 的顏色。

在做 B2C 網站的時候,雖然一般也是都會訂出 primary, secondaery 顏色的規範,但是難免還是有的時候會因應設計師的要求,或是其他的考量,需要用一些特別的顏色,或是顏色上的微調,以工程師的實作上來說,或是設計規範,我們是不希望有這樣的特例,但當這些規範與商業考量有一些衝突的時候,還是需要做一些妥協來因應這些需求。

以我個人來說,我也是希望能夠有 primary, secondary 來決定主題,但希望不限於這兩種顏色的傳入,所以在設計的時候我也會想要保留顏色調整的彈性,例如說我可能讓 color 這個 props 可以支援 primary, secondary 這樣的關鍵字之外,也能夠支援色票的傳入(ex: #1A73E8),但這樣的做法我覺得也見人見智,可能有人會覺得這樣的 props 會容易混淆,或是無法避開打錯字,但若真的有因為這樣而覺得很困擾的狀況,可以再看怎麼樣來避免,但目前我先簡單來做。

在 css 屬性當中,color 代表文字的顏色,background-color 代表背景色,一個按鈕當中,有許多地方需要配色,例如文字、背景、邊界(border)等等,為了避免混淆,我暫且把 Button 的主題色 props 叫做 themeColor。

另外還有一個顏色配色上的考量,就是可能深色背景色需要搭配淺色文字,淺色背景色需要搭配深色文字,否則文字會看不清楚,若給單一顏色是無法做到上面的調整,因此若有預設好的主題配色 primary, secondary 在使用起來其實也會比較方便。

<Button
  themeColor="primary" // primary | secondary | #1A73E8 | ...
>
  按鈕
</Button>

帶有圖示(icon)的按鈕

MUI 這邊的介面上設計了讓我們可以傳入 startIcon 以及 endIcon,也就是這個 Icon 不限於一定要放在文字之前還是之後。

Antd 這邊就只有設計一個 icon 的 props 讓我們可以傳入,所以我們只能在文字的左邊加入我們想要的 icon。

帶有 icon 的 button 在我個人的開發經驗上面是蠻常遇到的,而且 icon 真的不會只限於文字的左邊或右邊,所以我覺得 icon 可以支援出現在左邊和右邊是蠻重要的。

<Button
  startIcon={<Icon />}
>
  按鈕
</Button>

<Button
  endIcon={<Icon />}
>
  按鈕
</Button>

另一方面,因應 RWD 的設計,帶有 icon 的 button 在窄螢幕的狀態下,常常會需要變成只有 icon 的 button,所以 button 能夠支援只有 icon 沒有文字的樣式,也是蠻需要被考慮進去的。

比較一下 MUI 與 Antd 的 icon button,MUI 是需要另外再使用 IconButton 元件來做

import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';

<IconButton aria-label="delete">
  <DeleteIcon />
</IconButton>

而 Antd 是可以直接延續使用原本的 button ,把 children element 省略就可以做到

<Button
  icon={<PoweroffOutlined />}
/>

狀態屬性

我們在點擊發送表單的按鈕的時候,會需要處理一些不同的狀態,例如說可能有一些欄位是必填但是還沒被填寫,因此不希望使用者按下發送按鈕,這時我們需要 button 是 disabled 的狀態。

當按下發送按鈕的時候,因為前後端需要透過 API 溝通,因此要有一個非同步的狀態,例如讓使用者知道現在是 loading 中,因此我們也需要讓 button 有一個 loading 狀態。

因此我們看 MUI、Antd、Bootstrap 的按鈕上面,都有一個 disabled 的 props 讓我們可以傳入。

另外,在處理 loading 狀態的部分,Antd 他提供一個 loading 的 props 讓我們設置載入的狀態。

<Button
  loading
>
  按鈕
</Button>

而 MUI 及 Boostrap 看起來是希望你在 button children element 的地方自己處理 loading 的樣式,類似像下面這樣:

<Button>
  {isLoading && <Spinner />}
  按鈕
</Button>

我覺得這兩種介面都各有優缺,看自己的系統需要可以做選擇,如果很確定每一種 button 的 loading 樣式都一樣的話,我覺得用 loading props 傳入是比較方便,而且程式碼也簡潔、易讀。但如果需要比較客製化的彈性的話,可能在 button children element 的地方再自己依照需求去刻 loading 樣式會比較不會被限制住。

其他外觀屬性

有一些其他屬性,例如說像 Antd 裡面有 shape 屬性,來決定按鈕的形狀(ex: circle, round...),然後我看 MUI、Antd、Bootstrap 都有 size 這個屬性,來決定按鈕的大小。這些外型上的屬性我覺得也是看系統需要,如果前端工程師跟設計師之前彼此之間有講好一個默契,在 Guideline 上面有大、中、小這幾種 button 的 size,那我覺得這樣設計元件是會蠻方便的,系統也會比較統一,但我覺得這些屬性好像也不是這麼適合每一個系統,因為雖然有些系統會有大、中、小這些固定 size 的按鈕,但是有時候就是難免會出現一些惱人的特例,這部分我覺得是還蠻難的一個課題,像我在新創公司的經驗,為了因應快速調整、快速變化的需求,很難好好的把一些規範定下來之後再來設計這個系統,所以要遵守這些規範其實還蠻難的。但在老公司的經驗,整個團隊、開發流程也比較成熟,所以對於這種設計規範就比較講究。

如果是比較能遵守規範的系統,元件 props 設計就讓他可以傳入大、中、小這些固定的 size 就好,但若需要比較常因應一些變化,我覺得我會寫一個 BaseButton,把不會變的共用部份寫好,另外會變的部分再透過傳入 claaName 來調整。

<BaseButton
  className={props.className}
  {...otherProps}
>
  按鈕
</Button>

像我自己在開發的時候喜歡用 styled-components,這樣我就能夠繼承原本的 BaseButton,在這之上再客製化我在另一個地方特別需要的 button,類似像這樣:

import styled from 'styled-components';
import BaseButton from 'components/BaseButton';

const SpecialButton = styled(BaseButton)`
    // some styling here
`;

<SpecialButton
  {...props}
>
  按鈕
</SpecialButton>

事件屬性

事件屬性對一個 button 是蠻重要也蠻常用的,像是 onClick 事件。

我有看過有人刻意把自己寫的 button 事件參數改名為 handleClick,跟他討論他也很堅持他這樣的作法,因為他覺得比較喜歡這個名字,然後他也希望能跟一般的 button 元件做一個區分。想當然,在他的堅持之下,我們後面維護系統的路變得更加的坎坷,而且因為 button 是到處都會用到的元件,所以整個系統被這個東西污染得到處都是,已經到很難回頭的地步,我也很後悔當初沒有硬起來阻止這件事的發生。

我自己開發的建議是希望能夠直接沿用原本元件的事件屬性,因為

  • 假設你有些地方要 onClick,有些地方要 handleClick,這樣其實很容易會寫錯,若今天來了一位新同事,我相信他第一直覺一定會寫 onClick ,而不是 handleClick。
  • 由於除了 onClick 之外,也有很多其他的事件屬性,例如 onFocus, onBlur, onChange...等等,那是不是每個地方都要統一改名字叫做 handleXXX ? 若有一個地方沒改到,那事件屬性命名也就會產生不一致的問題,對於其他工程師要來維護這個元件會造成困擾。
  • 假設今天公司有多個專案,以 A、B 專案為例,A 專案設計了一個 button,裡面事件叫做 onClick,B 專案也設計了一個 button,裡面的事件叫做 handleClick。第一個問題是,我們在維護上會如同前面說的容易混淆寫錯。第二個是,假設今天公司要統一樣式,那要把 B 專案的 button 從底層抽換成 A 專案的 button,就會有不相容的問題,要修改也要花更多的成本。

介面設計

保留對 button 的使用習慣

介面設計上我自己也會希望盡量保留原本我們對 button 元件的使用習慣。
例如說,我們原本使用 button 的方式如下:

<button
  {...props}
>
  確認按鈕
</button>

目前我們所看到的原生 button 也是長這樣,MUI、Antd、Bootstrap 也都是這樣,所以我覺得我會希望把自己設計的 button 也能這樣被使用:

<CustomButtonA
  {...props}
>
  確認按鈕
</CustomButton>

而不是這樣:

<CustomButtonB
  {...props}
  text="確認按鈕"
/>

我之前也是看到有人把按鈕設計成上述 CustomButtonB 這樣的形式,他的理由是說希望按鈕的文字傳進去可以是固定的資料型別(這邊範例的狀況是使用 typescript 做型別的確保),但我個人的看法是覺得這樣的設計還蠻特立獨行的,除了違反我們一般的習慣之外,這樣的設計讓未來 button 的變化和擴充就只能透過 props 傳入來改變,而沒辦法好好善用 children element,如果 button 的內容不限於文字,例如我們 button 內的 icon 或是 loading 狀態希望透過 children element 來處理,就會比較難做到。對於他希望做型別確保這件事,我覺得雖然有他的好意,但後續衍伸的缺點確實還蠻困擾我的,所以我覺得這個優點真的有點難吸引我。

預計設計的介面

根據上述我自己的考量與評估(還有自己的喜好 XD),目前預計設面的介面如下表格:

屬性 說明 類型 默認值
variant 設置按鈕類型 contained, outlined, text
themeColor 設置按鈕的顏色 primary, secondary, 色票 pirmary
startIcon 設置按鈕左方圖示 node
endIcon 設置按鈕右方圖示 node
isLoading 載入中狀態 boolean false
isDisabled 禁用狀態 boolean false
children 按鈕的內容 node

當然元件的設計也沒有那麼一翻兩瞪眼,終歸一句話,我覺得還是要依據自己專案的狀況以及團隊的共識來設計會比較好,在設計的過程當中,「討論」是很重要的,若設計師設計自己的,工程師設計工程師的,PM 也按照他想像的開 spec,彼此各做各的,在未經討論取得共識的狀況下,最後哪一天突然把 spec 和設計圖拿出來,要求工程師一個 sprint 要做出符合他們期待的,那這樣對於整個專案來說我覺得就是個災難。

元件實作

基本樣式

我們採用的 CSS-in-JS 工具是 styled-components,首先我會在基礎的 button 上面給一些預設的樣式,這些預設的樣式主要是一些不會因為 props 傳入而有所改變的樣式,也就是不論你的 props 是什麼,大部分狀況下都需要共同擁有的樣式。例如按鈕預設的長寬、滑鼠 hover 上去的滑鼠圖示、圓角樣式...等等。

const StyledButton = styled.button`
  border: none;
  outline: none;
  min-width: 100px;
  height: 36px;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  border-radius: 4px;
  cursor: pointer;
  transition: color 0.2s, background-color 0.2s, border 0.2s, opacity 0.2s ease-in-out;

  &:hover {
    opacity: 0.9;
  }
  &:active {
    opacity: 0.7;
  }
`;

const Button = (props) => (
  <StyledButton {...props}>
    <span>{children}</span>
  </StyledButton>
);

變化模式 variant

variant 我們希望傳入的參數包含有 contained, outlined, text 這三種。所以我們需要依據這些參數來取得對應的樣式。

其中一種方式我們可以用 if...else... 的方式來處理,例如:

if (variant === 'contained') {
  return containedStyle;
} else if (variant === 'outlined') {
  return outlinedStyle;
} else if (variant === 'textStyle') {
  return textStyle;
} else {
  return containedStyle;
}

但是這樣寫我覺得有點冗長,而且假設除了上述三種 variant 之外,我們要做擴增,那就是需要再多寫一個 else...if... 判斷式。

另一個方式我們可以用物件的方式將 variant 以及對應的樣式用 key-value 的結構來儲存:

const variantMap = {
  contained: containedStyle,
  outlined: outlinedStyle,
  text: textStyle,
};

未來要擴增的時候,我們只需要增加這個 key-value 的對應即可。但要記得處理使用者不小心傳入不存在的 key 的情況:

const StyledButton = styled.button`
  //...other style 

  ${(props) => variantMap[props.$variant] || variantMap.primary}
`;

themeColor

themeColor 我們希望傳入的參數包含有 primary, secondary, 以及色票。所以我們先準備一下我們的主題色:

export const COLOR = {
  primary: '#1976d2',
  secondary: '#dc004e',
};

當然我們的主題色如果能夠用 ThemeProvider 來處理,那會是更漂亮,甚至我們能夠做到網站主題色的切換,但這邊我們為了方便講解,我們先用上述方式陽春的來處理。

因此,props 傳進來的時候,我們想要先統一轉換成合法的顏色代碼,傳進來的 themeColor 有幾種可能:

  • primary or secondary
  • 合法的顏色代碼,ex: #1976d2
  • 不小心把上述兩者打錯字,或是根本就傳一個不合法的字串

所以我們的邏輯是這樣的:

  1. 檢查是否為合法的顏色代碼?我們可以用 regular expression 來幫助我們檢查。
  2. 若合法,則直接回傳;若不合法,則檢查是否為保留字(primary, secondary)?
  3. 若是保留字,則將保留字轉換成對應的顏色代碼。
  4. 若都不是,則給他一個預設值,我們預設是 primary 的顏色代碼。
const makeBtnColor = (themeColor) => {
  /**
   * Color codes regular expression
   * https://regexr.com/39cgj
   */
  const colorRegex = new RegExp(/(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6})\b|(?:rgb|hsl)a?\([^)]*\)/);
  const isValidColorCode = colorRegex.test(themeColor.toLocaleLowerCase());
  return isValidColorCode ? themeColor : (COLOR[themeColor] || COLOR.primary);
};

const btnColor = makeBtnColor(themeColor);

透過上述方式,我們可以把 themeColor 轉換成一個合法的顏色代碼,我們用 btnColor 這個參數來儲存。有了 btnColor ,我們就可以把它提供給不同的 variant 來做顏色的調整。

isDisabled

按鈕的禁用狀態,按鈕的禁用狀態有兩個部分需要處理,一個是外觀,一個是行為。

顏色的處理上,只要是 disabled ,我們一律給他灰色,因此,上一段提到的 btnColor 我們要做一些小調整

const DISABLED_COLOR = '#dadada';
const btnColor = isDisabled ? DISABLED_COLOR : makeBtnColor(themeColor);

接著就是他滑鼠的 cursor 我們可以給他禁用圖標,而且由於禁用的按鈕不會有點擊事件,所以我們也取消讓人家覺得他可以點的樣式,例如 hover 及 active 的樣式。

const disabledStyle = css`
  cursor: not-allowed;
  &:hover, &:active {
    opacity: 1;
  }
`;

const StyledButton = styled.button`
  //...other style 

  ${(props) => (props.$isDisabled ? disabledStyle : null)}
`;

另外, onClick 事件我們希望在元件內就讓他 disabled,因為如果我們只有處理 disabled 的樣式,但是忘記處理 onClick 事件,就會讓禁用按鈕也觸發事件,這樣會讓人覺得很奇怪,所以比起每次從外面 onClick 的 props 來處理,不如在元件內就把他處理好,避免哪天自己不小心忘記而產生 bug。

<StyledButton
  {...props}
  onClick={isDisabled ? null : props.onClick}
>
  {children}
</StyledButton>

isLoading

按鈕的載入狀態我們讓他在文字的左邊有一個 circular progress

實作上的想法如下,當 isLoading 為 true 的時候,我們就顯示 circular progress,就是這麼單純,然後稍微調整一下他的大小、圖示與文字的對齊、間距就可以了:

const Button = (props) => (
  <StyledButton {...props}>
  {isLoading && (
    <StyledCircularProgress
      $variant={variant}
      $color={btnColor}
      size={16}
    />
  )}
    <span>{children}</span>
  </StyledButton>
);

考慮到樣式上變化的細節,當 variant 不同時,circular progress 的顏色也需要不同,這部分如果忘記處理就會看起來怪怪的,但基本上就是跟著文字的顏色走應該就沒錯了。

const StyledCircularProgress = styled(CircularProgress)`
  margin-right: 8px;
  color: ${(props) => (props.$variant === 'contained' ? '#FFF' : props.$color)} !important;
`;

另外值得一提的部分是, Button 會有 loading 狀態的時候,大部分的狀況是表單送出在等待 API response 的時候,所以在 loading 狀態,是否還允許使用者點擊按鈕繼續觸發 API request 呢?很多情境下其實我們不希望這樣的狀況發生。

如果這個 loading 瞬間完成,快到使用者無法點兩下,那倒還好,但如果真的都這麼快,好像我們也不用特別做一個 loading 狀態放在那邊告知使用者。

因此,若考慮到上述狀況,我們直接讓 loading 狀態的時候就 disable Button,其實也是蠻不錯的,這樣的機制要直接做在共用 Button 內?還是要由元件外面的條件來控制?我覺得也是一個值得討論的議題,但團隊有共識還是最重要的。

startIcon & endIcon

在按鈕文字的左邊、右邊放上 icon ,邏輯上跟 isLoading 差不多,就是 props 有傳入,就讓他出現,沒傳入,就不要 render 出來。

因為 startIcon & endIcon 都是由外部傳入的,因此樣式也可以由外部來控制,不會被綁死在元件當中。

甚至如果我們不喜歡這個 startIcon & endIcon ,我們直接把 icon 跟按鈕內容透過 children 傳進來,以這樣的架構來看也是可以做到的,不過既然我們都已經提供了 startIcon & endIcon ,非特殊狀況下,我們還是希望整個專案中能夠統一寫法:

const Button = (props) => (
  <StyledButton {...props}>
    /* 省略程式碼... */
    {startIcon && <StartIcon>{startIcon}</StartIcon>}
    <span>{children}</span>
    {endIcon && <EndIcon>{endIcon}</EndIcon>}
  </StyledButton>
);

客製化樣式

對於這個按鈕特殊情況下的樣式,我們也保留了客製化樣式的空間,例如我們允許從外面傳入 className 以及 style 這兩個 props,所以我們可以做到如下的操作,藉此來改變透過其他 props 無法調整的樣式:


Button 元件原始碼:
Source code

Storybook:
Button


下一篇
【Day02】數據輸入元件 - Switch
系列文
30 天擁有一套自己手刻的 React UI 元件庫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
xatm092
iT邦新手 5 級 ‧ 2022-10-16 21:37:20

不好意思請教一下,為什麼你的props的'.'之後要加一個'$'字號來取得變數值呢?是習慣嗎?因為蠻少看到這種把prop前面加'$'號的寫法,還是

這是用來取default值的方式?因為我把$字號拿掉後是可以正常運作的除了variant這個prop

和CircularProgress 中的variant 衝突需要改名外都是可以正常運作。

Taiming iT邦研究生 5 級 ‧ 2022-10-16 23:33:59 檢舉

您好,感謝您的回覆!
在 styled-components v5.1 之後支援這個功能,加上「$」符號為命名的變數,可以讓此 props 變數成為一個臨時的變數,只會停留在 styled-components 這一層,可以防止他傳遞到下一層的 React 節點。

https://styled-components.com/docs/api#transient-props

xatm092 iT邦新手 5 級 ‧ 2022-10-18 18:30:03 檢舉

非常謝謝講解!

我要留言

立即登入留言