# 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;
} */
```