# styled-components 筆記 此筆記是基於 zerotomastery.io 推出 React 課程 [Complete React Developer in 2022](https://www.udemy.com/course/complete-react-developer-zero-to-mastery/) 所撰寫。 ## 136. Introducing Styled-Components **本小節重點:** 將 `navigation.styles.scss` 改為 `navigation.styles.jsx` 並使用在 `navigation.components.jsx` 中。 ### 什麼是 styled-components? styled-components 這個第三方套件讓我們可以將原本為 .css 或 .scss 的檔案改為元件的寫法(副檔名為 .js 或 .jsx 的檔案),透過 JS 動態產生 unique 的 class name,元件跟元件就不會發生因人為命名失誤而產生的 CSS class name 碰撞。 ### 套件安裝 `yarn add styled-components` ### 使用方式 1. 建立一個副檔名為 .js 或 .jsx 的檔案,因為要做一個 **styled-component。** 2. `import styled from 'styled-components';` 3. **語法** ```jsx import { Link } from 'react-router-dom'; const 自訂 Component 名稱 = styled.HTML 標籤` // 這裡寫 CSS `; const 自訂 Component 名稱 = styled(React 內建 component 或自訂的 compoent)` // 這裡寫 CSS `; /* styled.HTML 標籤可以為 styled.div styled.button styled.span 等等各種 HTML tag */ ``` 通常 styled-component 會 export 出去讓其他元件使用,所以提供實際範例如下: ```jsx import { Link } from 'react-router-dom'; export const NavigationContainer = styled.div` height: 70px; width: 100%; `; export const LogoContainer = styled(Link)` width: 70px; padding: 25px; `; ``` 4. 接著在要用到這些 styled-components 元件中引用它們 ```jsx import { NavigationContainer, LogoContainer, } from './navigation.styles'; const Naigation = () => { const { currentUser } = useContext(UserContext); const { isCartOpen } = useContext(CartContext); return ( <> <NavigationContainer> <LogoContainer to="/"> <CrwnLogo className="logo" /> </LogoContainer> // 略... </Fragment> ); }; ``` ### 補充 - styled-components 標籤帶有一個 as 屬性,值為字串,可以填入 HTML 的 tag 去決定它被渲染出來樣子。假如承上範例,我們將 `<NavigationContainer>` 寫為 `<NavigationContainer as="section">`,`<NavigationContainer>` 在畫面上就會被渲染為 `<section>` 標籤(原本是定義為 styled.div)。 - `styled()` 可以再繼承一個宣告好的 styled-compoent。 - styled-component 可以再巢狀寫入 CSS,或者用 `${}` 寫入另一個 styled-component。 - styled-component 也能接受 props 的值 ⇒ 使用一個匿名函式去接受 props 值並 return 你想要的 CSS 屬性值字串。 ```jsx import styled from 'styled-components'; export const BackgroundImage = styled.div` background-image: ${({ imageUrl }) => `url(${imageUrl})`}; `; ``` - `import styled, { css } from 'styled-components';` styled-compoents 中的 css 模塊用來記錄一段 CSS 並可以注入在其他 styled-components 中。範例如下: ```jsx import styled, { css } from 'styled-components'; const mainColor = 'black'; const shrinkLabelStyles = css` top: -14px; font-size: 12px; color: ${mainColor}; `; export const Input = styled.input` &:focus { ${shrinkLabelStyles} } `; /* styled-compoents 中的 css 模塊可以與 scss 中的 @mixin 對應 @mixin shrinkLabel { top: -14px; font-size: 12px; color: $main-color; } */ ``` ## 137. Styled-Components - Button **本小節重點:** 將 `button.styles.scss` 改為 `button.styles.jsx`,並在 `sign-in-form.component.jsx`、`product-card.component.jsx` 使用。 1. `button.styles.jsx` ```jsx import styled from 'styled-components'; export const BaseButton = styled.button` // 略... `; // styled() 可以再繼承一個宣告好的 styled-compoent。 export const GoogleSignInButton = styled(BaseButton)` // 略... `; export const InvertedButton = styled(BaseButton)` // 略... `; ``` 1. `button.component.jsx` 我們這邊將三種型態的 button title 以物件的方式宣告為 `BUTTON_TYPE_CLASSES`,目的是可以藉由「.」運算子來自動辨識對應的字串,減少人為打錯字的機會。接著建造一個 getButton helper function 依據 buttonType 傳入的值來決定要回傳顯示哪一個 styled-component。 ```jsx import { BaseButton, GoogleSignInButton, InvertedButton, } from './button.styles'; export const BUTTON_TYPE_CLASSES = { base: 'base', google: 'google-sign-in', inverted: 'inverted', }; const getButton = (buttonType = BUTTON_TYPE_CLASSES.base) => { // 物件實體立即用 [] 取值寫法,與下面 switch 同義 // ({ // [BUTTON_TYPE_CLASSES.base]: BaseButton, // [BUTTON_TYPE_CLASSES.google]: GoogleSignInButton, // [BUTTON_TYPE_CLASSES.inverted]: InvertedButton, // }[buttonType]); switch (buttonType) { case BUTTON_TYPE_CLASSES.base: return BaseButton; case BUTTON_TYPE_CLASSES.google: return GoogleSignInButton; case BUTTON_TYPE_CLASSES.inverted: return InvertedButton; } }; const Button = ({ children, buttonType, ...otherProps }) => { const CustomButton = getButton(buttonType); return <CustomButton {...otherProps}>{children}</CustomButton>; }; export default Button; ``` ## 138. Styled-Component - Cart Dropdown **本小節重點:** 將 `cart-dropdown.styles.scss` 改為 `cart-dropdown.styles.jsx` 並使用在 `cart-dropdown.components.jsx` 中。 - `cart-dropdown.styles.jsx` ```jsx import styled from 'styled-components'; import { BaseButton, GoogleSignInButton, InvertedButton } from '../button/button.styles'; export const CartDropdownContainer = styled.div` // 略... // styled-component 可以再巢狀寫入 CSS,或者用 ${} 寫入另一個 styled-component ${BaseButton}, ${GoogleSignInButton}, ${InvertedButton} { margin-top: auto; } `; // 略... ``` ## 139. Styled-Component - Directory Item & Cart Icon **本小節重點:** - 將 `cart-icon.styles.scss` 改為 `cart-icon.styles.jsx` 並使用在 `cart-icon.components.jsx` 中。 - ShoppingIcon 本身是一張 SVG 圖檔被我們引入作為 ReactComponent 再宣告成 styled-component。 - 將 `directory-item.styles.scss` 改為 `directory-item.styles.jsx` 並使用在 `directory-item.components.jsx` 中。 - styled-component 也能接受 props 的值 ⇒ 使用一個匿名函式去接受 props 值並 return 你想要的 CSS 屬性值字串。 ```jsx import styled from 'styled-components'; export const BackgroundImage = styled.div` background-image: ${({ imageUrl }) => `url(${imageUrl})`}; `; ``` ## 140. Styled-Component - Form Input Component **本小節重點:** - 將 `form-input.styles.scss` 改為 `form-input.styles.jsx` 並使用在 `form-input.components.jsx` 中。 - `import styled, { css } from 'styled-components';` styled-compoents 中的 css 模塊用來記錄一段 CSS 並可以注入在其他 styled-components 中。範例如下: ```jsx import styled, { css } from 'styled-components'; const mainColor = 'black'; const shrinkLabelStyles = css` top: -14px; font-size: 12px; color: ${mainColor}; `; export const Input = styled.input` &:focus { ${shrinkLabelStyles} } `; /* styled-compoents 中的 css 模塊可以與 scss 中的 @mixin 對應 @mixin shrinkLabel { top: -14px; font-size: 12px; color: $main-color; } */ ```