Compound Component Pattern
Overview
Compound Component เป็นรูปแบบการออกแบบใน React ที่ช่วยให้สร้าง components ที่ยืดหยุ่นและปรับแต่งได้ โดยการแยกส่วนประกอบต่างๆ ที่มีความสัมพันธ์กันออกเป็นหลายๆ child component ที่สามารถทำงานร่วมกันได้ผ่าน parent component เดียวกัน
Concept
Compound Component ใช้ Context API และ React.Children API เพื่อ:
- สร้าง parent component ที่จัดการ state หลัก
- ให้ child components เข้าถึง state หรือฟังก์ชันผ่าน context
- ทำให้โครงสร้าง component ชัดเจนและง่ายต่อการปรับแต่ง
Advantages
- Flexibility: สามารถปรับแต่งโครงสร้างและการทำงานได้ง่าย
- Reusability: นำส่วนประกอบกลับมาใช้ซ้ำได้ในหลายๆ สถานการณ์
- Separation of Concerns: แยกการจัดการ state ออกจาก UI
- Maintainability: ทำให้โค้ดอ่านง่ายและจัดการง่ายขึ้นเมื่อแอปพลิเคชันเติบโต
When to Use Compound Component
- เมื่อส่วนประกอบมีความสัมพันธ์กัน เช่น Tabs, Modals, Dropdowns, หรือ Accordions
- ต้องการให้ parent component จัดการ state และส่งข้อมูลไปยัง child component โดยไม่ใช้ prop drilling
- ต้องการเพิ่มความยืดหยุ่นให้ผู้ใช้งานปรับแต่งหรือขยายความสามารถของ component ได้ง่าย
Example: Tab and TabContent
ขอยกตัวอย่างการใช้งานกับ Tab และ TabContent
จากรูป component ด้านบน
Context API
- สำหรับการ share state ใน
Parent component
Parent Component (Tab
)
- ใช้
TabContext
เพื่อจัดการactiveIndex
(สถานะของ tab ที่เลือกอยู่) - ให้ child components ใช้ข้อมูลและฟังก์ชันผ่าน Context
Child Components
Tab.TabMenu
: Render ปุ่ม tab ต่างๆ และเปลี่ยนactiveIndex
เมื่อผู้ใช้คลิกTab.Panel
: แสดงเนื้อหาเฉพาะ tab ที่เลือก
Implementation
Code Example
Tab.tsx
import React, { useState, createContext, useContext } from 'react'
// Create Context API
const TabContext = createContext()
// Custom hook for accessing Tab context
const useTabChange = () => {
const context = useContext(TabContext)
if (!context) {
throw new Error('TabProvider not implemented')
}
return context
}
// Parent component: Tab
const Tab = ({ children }) => {
const [activeIndex, setActiveIndex] = useState(0)
return (
<TabContext.Provider value={{ activeIndex, setActiveIndex }}>
{children}
</TabContext.Provider>
)
}
// Child component: TabMenu
Tab.TabMenu = ({ children }) => {
const { activeIndex, setActiveIndex } = useTabChange()
return (
<div>
{React.Children.map(children, (child, index) => (
<button
onClick={() => setActiveIndex(index)}
style={{ fontWeight: activeIndex === index ? 'bold' : 'normal' }}
>
{child}
</button>
))}
</div>
)
}
// Child component: TabPanel
Tab.Panel = ({ children, index }) => {
const { activeIndex } = useTabChange()
return activeIndex === index ? <div>{children}</div> : null
}
export default Tab
App.tsx
import React from 'react'
import Tab from '@src/component/ui/Tab'
const App = () => {
return (
<Tab>
<Tab.TabMenu>
<span>Tab 1</span>
<span>Tab 2</span>
<span>Tab 3</span>
</Tab.TabMenu>
<Tab.Panel index={0}>Content for Tab 1</Tab.Panel>
<Tab.Panel index={1}>Content for Tab 2</Tab.Panel>
<Tab.Panel index={2}>Content for Tab 3</Tab.Panel>
</Tab>
)
}
export default App
Key Features
- Dynamic Rendering: ใช้
React.Children.map
เพื่อกำหนดโครงสร้าง child components - Context Management: ใช้
useContext
เพื่อแชร์ข้อมูลระหว่าง parent และ child components - Encapsulation: การจัดการ state อยู่ใน parent component เพียงที่เดียว
Best Practices
- ใช้ Prop Validation เพื่อตรวจสอบ props ที่ส่งมาใน child components
- ออกแบบ child components ให้แยกความรับผิดชอบอย่างชัดเจน
- ทำให้ Component Interface (API) ง่ายต่อการเข้าใจ เช่น ชื่อ method หรือ prop ที่ชัดเจน
- ใช้ TypeScript เพื่อลดข้อผิดพลาดและทำให้โค้ดอ่านง่ายขึ้น
Conclusion
Compound Component เป็นรูปแบบการออกแบบที่ช่วยเพิ่มความยืดหยุ่น ความสะอาด และความสามารถในการจัดการโค้ดในโปรเจกต์ React โดยเหมาะสำหรับส่วนประกอบที่มีการเชื่อมโยงกันอย่างใกล้ชิด เช่น Tabs, Dropdowns, และ Modals
Next article>
Island Architecture ใน Astro ทำงานอย่างไร?
P
Writter By @Patradanai
จดบันทึกเรื่องราวที่เกิดขึ้นในชีวิต และเรื่องราวที่เกิดขึ้นในการทำงาน