จด 5 เทคนิคสำหรับการเริ่มต้นใช้งาน styled-components

May 30, 2021

styled-components ก็คือ library สำหรับใช้ css ใน react อีกรูปแบบหนึ่ง ก่อนหน้าที่จะลองเปลี่ยนมาใช้ library นี้ผมได้ใช้ material-ui version 4 มาก่อนซึ่งจะเป็นรูปแบบของ CSS-in-JS ซึ่งจะเจอประเด็นเรื่องการทำ dynamic styles ที่ลำบากพอสมควรและพอดีได้มาเจอและอ่าน document ของ styled-components คร่าว ๆ แล้วเข้าไปเจอ motivation ที่เขียนไว้ว่า “optimize the experience for developers” จึงตัดสินใจลองใช้ดู

หลังจากเริ่มใช้ styled-components มาสักพักก็เป็นเวลาอันสมควรเลยจะขอจดบันทึกและแชร์เทคนิคไว้หน่อยประมาณนี้

Conditional rendering

เมื่อเราต้องการสร้าง component ที่ dynamic ขึ้นมาหน่อยเราสามารถ ใช้วิธีที่เรียกว่า “interpolation” หรือการแทรก function เข้าไป template literals

ตั้งแต่เขียน styled-components ผมต้องย้ายปุ่มเปลี่ยนภาษาไปเป็น alt+tab เพราะต้องใช้ grave ในการทำ styles

ซึ่งใน function นั้นเราจะสร้างเงื่อนไขและการคืนค่าออกมาเป็น string เพื่อใช้เป็น value ของ css property โดย props ที่เรา pass เข้ามาก็เปรียบเสมือน input

ตัวอย่างแรก เมื่อเราต้องการปุ่มที่เมื่อ selected เป็น true จะทำการเปลี่ยนสีของปุ่มเป็นสี green

const App = () => {
  return (
    <div>
      <Item selected />
      <Item />
    </div>
  )
}

const Item = styled.button`
  color: ${props => (props.selected ? "green" : "white")};
`

ตัวอย่างที่สอง ถ้าเราต้องการ dynamic หลายๆ css property พร้อมกันละ 😄

import { css } from "styled-components"

const App = () => {
  return <Typo noWrap>Text</Typo>
}

❌ ❌ ❌
const Typo = styled.p`
  white-space: ${({ noWrap }) => noWrap && "nowrap"};
  overflow: ${({ noWrap }) => noWrap && "hidden"};
  text-overflow: ${({ noWrap }) => noWrap && "ellipsis"};
`
✅ ✅ ✅
const Typo = styled.p`
  ${({ noWrap }) =>
    noWrap &&
    css`
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    `}
`

ccs tag ที่ครอบระหว่าง template literals อาจจะไม่จำเป็นในบางกรณี เช่นในตัวอย่างไม่ต้องมีก็ได้สามารถศึกษาเพิ่มได้ตาม Link

Theming

การที่จะทำ styles ให้มีความเป็นระเบียบ มี pattern จำเป็นอย่างยิ่งที่จะต้องทำระบบ theme ขึ้นมา เช่น การกำหนด primary color, spacing หรือ font-size เป็นต้น ซึ่งใน styled-components ก็สามารถทำ theme ได้เช่นเดียวกับ tool ตัวอื่น ๆ และทำได้ง่ายมาก

  1. วาง ThemeProvider ที่ top สุดของ component (ถ้าเคยใช้ context จะคุ้นเคยกับท่านี้)
  2. สร้าง theme object และ pass เข้าไปที่ ThemeProvider
  3. component ที่อยู่ภายใต้ provider นั้นสามารถเข้าถึง theme object ผ่านท่า interpolation ได้เลยตามตัวอย่าง
import styled from "styled-components"
import { ThemeProvider } from "styled-components"

const theme = {
  colors: {
    primary: "blue",
    secondary: "green",
  },
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Button>Label</Button>
    </ThemeProvider>
  )
}

const Button = styled.button`
  background-color: ${({ theme }) => theme.colors.primary};
`

การทำ theme ใน styled-components นั้นไม่ยากแต่สิ่งที่ยากคือเราต้องกำหนดค่าอะไรบ้างใน theme ของเราเพื่อให้ในแต่ละ component มี design ที่สอดคล้องกัน (ผมลองอ้างอิงจาก lib เพื่อนบ้านอย่าง material-UI)

การสร้าง component ใหม่จาก component เดิมง่าย ๆ

ในการสร้าง component หนึ่งให้ dynamic และสามารถ reuse ได้ก็เป็นเรื่องที่ดีตามหลักของ clean code แต่ถ้าถึงจุดหนึ่งและมีความรู้สึกว่าการทำ reusable component ทำให้ logic ข้างในซับซ้อนเกินไป ผมก็เลือกที่จะสร้าง component ใหม่โดย inherit styles จาก component เดิมไปสร้างเป็น component ใหม่และเติมสิ่งที่เราต้องการเปลี่ยนแปลงเข้าไปก็เป็นอีกวิธีหนึ่งที่ดีเพื่อลดความซับซ้อนและยังเป็น reusable component อยู่

const App = () => {
  return (
    <>
      <Button>Label</Button>
      <SuperButton>Label</SuperButton>
    </>
  )
}

const Button = styled.button`
  background-color: blue;
`

const SuperButton = styled(Button)`
  width: 120px;
  height: 80px;
  background-color: yellow;
`

การ extend styles มาสร้าง component มีประโยชน์ในหลายสถานการณ์ เช่น เราต้องการเพิ่มเติมความสามารถให้ component ที่คนอื่นสร้างไว้และไม่อยากไปแก้ไข code เดิมก็ใช้วิธีนี้เข้าไปช่วยได้

💡 ในบ้างครั้งการกลับไป re-design component เดิม ก็เข้าท่ากว่านะ

“className” prop is important

ถ้าสรุปการสร้าง component จาก styled-components จะมี 3 วิธีหลัก ๆ คือ

  1. สร้างจาก html tagname styled.div
  2. สร้างโดยการ extend จาก styled function styled(Button)
  3. สร้างจาก other component ตามตัวอย่างด้านล่าง

วิธีการนี้จะแตกต่างจากการ extend styles ตรง ๆ จากหัวข้อก่อนหน้านี้เล็กน้อย

import styled from "styled-components"

const StyledButton = ({ className }) => {
  return <Button className={className}>Label</Button>
}

const Button = styled.button`
  background-color: blue;
`

วิธีการสร้าง component แบบนี้จะเกิดบ่อยที่สุด จะเห็นว่าเราสร้าง StyledButton component จากนั้นจะถูกนำไปใช้ใน Area component โดยเราต้องการเพิ่มสีเข้าไปใน StyledButton ด้วยโดยที่ไม่ต้องการให้แก้ไขใน component เดิม

const Area = styled(StyledButton)`
  width: 120px;
  height: 80px;
  background-color: yellow;
`

const App = () => {
  return (
    <>
      <StyledButton>Label</StyledButton>
      <Action>Label</Action>
    </>
  )
}

📌 สิ่งสำคัญคือห้ามลืมใส่ className props ที่ StyledButton (ตอนครั้งแรกผมติดตรงนี้สักพักเลยเพราะลืมใส่)

“as” prop

ปกติแล้วเมื่อเราจะสร้าง component ข้างในก็จะมี HTML element เป็นส่วนประกอบ เช่น Button ก็จะมี <button /> หรือ textbox ก็จะมี <input />

แต่ถ้าเราต้องการ component อีกประเภทหนึ่งละ ที่ข้างในสามารถเปลี่ยน HTML element ได้ตาม condition เช่น <Typography /> ที่จะสามารถเปลี่ยนข้างในให้เป็น h1, h2, p หรือ จะเป็น element อะไรก็ได้ที่เราต้องการเกี่ยวกับการ display text ตามจุดประสงค์ของการนำ component ไปใช้แต่ styles ข้างในจะยังคงใช้เหมือนกัน

คำตอบ คือใช้ "as" เราจะสามารถสลับ HTML element ได้

import styled from "styled-components"

const Typo = styled.div`
  color: red;
  font-weight: bold;
`

const App = () => {
  return (
    <>
      <Typo as="h1">Label</Typo>
      <Typo as="h5">Label</Typo>
      <Typo as="p">Label</Typo>
    </>
  )
}

ความหมายเพิ่มเติมของ “as” polymorphic prop

แถมอีกนิด

ถ้าใช้งาน vs code สามารถติดตั้ง extension ที่ชื่อ vscode-styled-components ช่วยในการ highlight syntax ทำให้ code เราสวยงามขึ้นและอ่านง่าย






web picture

I'm Bulagorn Sasanthei
a lad who is interested in React, including TypeScript.