🛑 1. The Problem First: "Component มอนสเตอร์ที่หยุดกินไม่ได้"
ลองนึกถึงวันที่คุณสร้าง Component Button หรือ Card ที่ตอนแรกดูเรียบง่าย แต่พอเวลาผ่านไป:
// ❌ Naive Approach: สาดทุุกอย่างเป็น Boolean Props
<MyCard
header="Title"
isFluid={true}
hasShadow={false}
showCloseButton={true}
isEditing={mode === 'edit'}
withAnimation={true}
noPadding={false}
// ... อีก 20 อันที่ตามมาในอนาคต
/>
// 🌋 พัง! โค้ดข้างในของคุณจะเต็มไปด้วย if-else มหาศาล
# และเมื่อต้องเพิ่มพฤติกรรมใหม่ คุณแทบจะ 'ไม่กล้าแก้'
# เพราะกลัวไปกระทบโหมดอื่นๆ นี่คือจุดเริ่มต้นของ Technical Debt ครับ
ปัญหา: การดีไซน์แบบ "Configuration-driven" (ส่งธงเข้าไปคุมข้างใน) ทำให้ Component ของคุณขาดความยืดหยุ่น และที่สำคัญคือมัน "เดาใจยาก" ว่าอะไรจะเกิดขึ้นถ้าเราส่ง Prop 3 อย่างที่ขัดแย้งกันเข้าไปพร้อมกันครับ
💡 2. Real-Life Analogy: การสั่งอาหารแบบตามสั่ง vs ชุดคอมโบ้
- Boolean Prop Proliferation: เหมือน "เซ็ตเมนูแบบบังคับ". คุณสั่งเซ็ต A แต่คุณไม่เอาถั่ว ไม่เอาหอมใหญ่ และอยากเปลี่ยนน้ำเป็นน้ำส้ม ร้านต้องเขียนเงื่อนไขยุ่งยากในครัวเพื่อตอบสนองคุณ
- Composition (Compound Components): เหมือน "การตักอาหารใน Buffet หรือ Subway". ร้านเตรียมวัตถุดิบแยกกันไว้ (Bread, Meat, Veggies) แล้ว "คุณ" (ผู้ใช้) เป็นคนเลือกว่าจะหยิบอะไรมาวางก่อนหลัง ถ้าไม่ชอบมะเขือเทศ ก็แค่อย่าหยิบราดลงไป ง่ายๆ แค่นั้นเองครับ
🚀 3. Execution Journey: มหากาพย์การแยกส่วนและประกอบใหม่
Senior จะใช้เทคนิค "Inversion of Control" คือการคืนอำนาจให้ผู้ซื้อเป็นคนตัดสินใจว่าหน้าตาจะเป็นอย่างไร
🛠 Step-by-step:
- The Context Core: สร้าง
Contextเพื่อเชื่อมความสัมพันธ์ระหว่าง Component แม่และลูกแบบลับๆ (ไม่ต้องส่ง Props ผ่านคนกลาง) - The Sub-components: แตก UI ออกเป็นส่วนเล็กๆ เช่น
Composer.Header,Composer.Input,Composer.Footer - The Shared Logic: ให้ลูกๆ ดึงข้อมูลหรือคำสั่งมาจาก Context กลาง เพื่อให้ทำงานร่วมกันได้อย่างเป็นระบบ
- The Flexible Layout: อนุญาตให้ผู้ใช้วางลูกๆ สลับที่กัน หรือใส่โค้ดแปลกปลอม (Children) ลงไปแทรกได้ทุุกจุด
// ✅ Best Practice: การใช้งานแบบ Compound Components
function UserPost() {
return (
<Composer>
<Composer.Header user={currentUser} />
<Composer.Content placeholder="วันนี้คุณคิดอะไรอยู่..." />
<Composer.Actions>
{/* เราสามารถแทรกปุ่ม Custom ของเราเองได้ง่ายๆ ตรงนี้เลย */}
<CustomEmojiPicker />
<Composer.SubmitButton />
</Composer.Actions>
</Composer>
);
}
🪤 4. The Junior Trap: โรค "Prop Drilling" (พนักงานส่งเอกสารมือหงิก)
จูเนียร์มักจะส่งข้อมูลจากบนสุดลงไปล่างสุดผ่าน Component ตรงกลางหลายๆ ชั้น:
// ❌ Junior Trap: ส่งต่อ Props รัวๆ
<GrandParent theme={theme}>
<Parent theme={theme}>
<Child theme={theme} />
</Parent>
</GrandParent>
// 🌋 พัง! ถ้าวันหนึ่งคุณต้องการเปลี่ยนชื่อ Prop จาก theme เป็น color
# คุณต้องไล่แก้ทุุกไฟล์ที่มันวิ่งผ่าน
# ทั้งที่คนกลาง (Parent) ไม่ได้ใช้ข้อมูลนั้นเลยสักนิด
ระวัง: ทุุกชั้นที่ Props วิ่งผ่าน คือจุดเสี่ยงที่จะเกิดบั๊กและทำให้การ Refactor ล่าช้า ✅ การแก้ไข: ใช้ React Context ร่วมกับ Compound Components เพื่อให้คนรับข้อมูล "ดึงตรง" จากฐานข้อมูล (Context) ได้เลยครับ
⚖️ 5. The Why Matrix: Props-driven vs Composition
| หัวข้อ | Props-driven (Naive) | Composition (Senior) |
|---|---|---|
| ความยืดหยุ่น | ต่ำ (ต้องรอคนเขียนเพิ่ม Prop) | 🚀 สูงสุด (ผู้ใช้วางเองได้) |
| ความซับซ้อนภายใน | 🌋 สูง (เต็มไปด้วย if-else) | 💎 ต่ำ (Component ย่อยทำหน้าที่เดียว) |
| การทดสอบ (Testing) | ยาก (Case เยอะมาก) | ✅ ง่าย (เทสแยกกันรายตัว) |
| เหมาะกับ | Component ที่เน้นความง่ายที่สุด | Component กลางที่ใช้ทั้้งโปรเจกต์ |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "ความยืดหยุ่นที่ดีที่สุด ไม่ใช่การเตรียม Option ไว้ให้เยอะ แต่คือการออกแบบให้เครื่องมือนั้นสามารถ 'ร่วมมือ' กับสิ่งอื่นได้". เลิกควบคุมทุกอย่างจากข้างบน แล้วปล่อยให้ Composition ทำหน้าที่ของมัน งานของคุณจะเบาลงและทรงพลังขึ้นอย่างมหาศาลครับ!