

สร้าง Component ครั้งเดียว ใช้ได้ทุกที่ – ลดความซ้ำซ้อน เพิ่มประสิทธิภาพงานพัฒนา
เรียนรู้แนวทางออกแบบ Reusable Component ที่ทำให้โค้ดสะอาด ลดการซ้ำซ้อน และปรับตัวได้กับทุกโปรเจกต์
นักพัฒนาหลายคนอาจคุ้นเคยกับการเขียนโค้ด UI อยู่แล้ว เช่น ปุ่มล็อกอิน ปุ่มบันทึก หรือกล่องแสดงข้อความ เป็นต้น UI เหล่านี้มักจะมีดีไซน์ที่คล้ายกัน แต่ต่างกันเล็กน้อย เช่น สีหรือขนาด ซึ่งเราก็สามารถแก้ไขค่าเหล่านั้นได้ตรง ๆ อย่างไม่ยากเย็น แต่เมื่อโปรเจกต์เราใหญ่ขึ้น ความซ้ำซ้อนเหล่านี้จะกลายเป็นภาระหนักในการบำรุงรักษา และเป็นแหล่งที่มาของ bug ได้ง่าย
เพื่อแก้ปัญหาเหล่านี้ แนวคิด Reusable Component จึงมีความสำคัญมากในงานพัฒนา ไม่ว่าจะเป็นการเขียนด้วย React, Vue, Angular หรือแม้แต่ Laravel ที่มี Blade Components ก็ตาม การสร้างเพียงครั้งเดียวแต่สามารถนำไปใช้ซ้ำในหลายที่ ไม่เพียงช่วยลดภาระ แต่ยังทำให้ทีมทำงานอย่างมีระบบและคุณภาพมากยิ่งขึ้น
ในบทความนี้ เราจะมาดูกันว่า Reusable Component คืออะไร หลักการออกแบบที่ดีควรเป็นแบบไหน รวมถึงวิธีการสร้างจริง พร้อมทั้งตัวอย่างโค้ด และแนวทางปฏิบัติที่ช่วยให้คุณสร้างโค้ดที่สะอาดและมีประสิทธิภาพต่อไป
💡 Reusable Component คืออะไร#
Reusable Component คือ Component สำหรับแสดงผลต่อผู้ใช้งาน (UI Component) ที่สามารถนำไปใช้ซ้ำได้ในหลาย ๆ ที่ โดยไม่ต้องเขียนโค้ดใหม่ทุกครั้ง เช่น ปุ่ม (Button), ฟอร์มกรอกข้อมูล (Form Field), การ์ด (Card), หรือ Modal เป็นต้น
🛠️ หลักการออกแบบ#
ในการออกแบบ Reusable Component ที่ดี คือการออกแบบ Component โดยคำนึงถึงหลักการต่อไปนี้
-
Single Responsibility Principle (SRP) - แต่ละ Component ควรทำหน้าที่เฉพาะ เช่น ปุ่ม (Button) ควรทำหน้าที่สำหรับแสดงผลและรับ event กด เท่านั้น ไม่ควรผูกกับ logic อื่นที่ไม่เกี่ยวข้อง
-
Configurable - Component ควรออกแบบให้สามารถปรับแต่งได้ เช่น ผ่าน Props, Slot, Parameter หรือ Attribute โดยไม่ต้องเขียนใหม่
-
Composable - Component ควรจะสามารถนำมาประกอบกับ Component อื่นได้โดยง่าย เช่น ฟอร์มหนึ่งชุดอาจประกอบด้วย Component Input, Label และ Button
-
Consistency - Component ควรสร้างความสม่ำเสมอให้กับ UI ทั้งระบบ เพื่อประสบการณ์ของผู้ใช้ที่ดี
⚠️ ปัญหาที่เกิดขึ้นเมื่อไม่ใช้ Reusable Component#
จากข้อดีต่าง ๆ ที่ได้เกริ่นไปแล้ว หากระบบที่พัฒนาอยู่ไม่ได้คำนึงถึงหลักการ Reusable Component แล้วอาจเกิดปัญหาขึ้นได้ดังนี้
- โค้ดซ้ำซ้อน (Duplication Code) - มีโค้ดชุดเดียวกันซ้ำในหลายไฟล์ ทำให้โปรเจกต์ใหญ่ขึ้นโดยไม่จำเป็น
- บำรุงรักษายาก (Maintenance Overhead) - หากต้องเปลี่ยนสีหรือรูปแบบปุ่มทั้งโปรเจกต์ ต้องแก้ทุกไฟล์ที่เคยใช้ ทำให้มีโอกาสตกหล่นหรือผิดพลาด
- เกิดความไม่สอดคล้องกันของ UI (Inconsistency UI) - UI บางจุดอาจเกิดความผิดพลาดให้มีขนาดหรือสีที่ต่างกัน ทำให้เกิดความสับสนต่อผู้ใช้งาน
- ข้อเสียต่อการพัฒนาระบบ - การไม่ออกแบบ Reusable Component อาจทำให้การพัฒนาใช้เวลามากกว่าที่ควรจะเป็น หรืออาจเกิดความผิดพลาดได้ง่ายกว่า
🚀 ตัวอย่างการออกแบบ Reusable Component#
สมมุติว่าเราต้องการสร้าง UI ของปุ่มขึ้นมา 1 ปุ่มเพื่อใช้งาน โดยใช้ CSS framework เป็น tailwindcss เราอาจคำนึงถึงโอกาสที่ปุ่มนี้จะถูกเรียกใช้ในจุดอื่นและวางโครงสร้างไว้ดังนี้
type ButtonProps = {
variant?: "primary" | "secondary";
size?: "sm" | "md" | "lg";
children: React.ReactNode;
};
export const Button = ({
variant = "primary",
size = "md",
children,
}: ButtonProps) => {
const base = "rounded px-4 py-2 font-semibold";
const variants = {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-200 text-black",
};
const sizes = {
sm: "text-sm py-1",
md: "text-base py-2",
lg: "text-lg py-3",
};
return (
<button className={`${base} ${variants[variant]} ${sizes[size]}`}>
{children}
</button>
);
};tsxจากตัวอย่างจะเห็นว่าปุ่มที่เราสร้างขึ้น สามารถกำหนดสีหรือขนาดผ่าน Props ได้เลยโดยไม่ต้องแก้ไขโค้ดอีก หรือหากต้องการปรับปรุงเพิ่มเติมในอนาคตก็สามารถทำได้โดยง่าย
🎯 เพิ่มประสิทธิภาพการใช้งานด้วย CVA (Class Variance Authority)#
CVA (Class Variance Authority) คือ utility function สำหรับจัดการ className ของ Tailwind ที่มี variants (เช่น intent, size, state) ได้อย่างเป็นระบบและปลอดภัยกว่าเขียน string ตรงๆ หากต้องการใช้งาน CVA ก็สามารถทำได้โดยง่ายดังนี้
import React from "react";
import { cva, type VariantProps } from "class-variance-authority";
const button = cva("rounded px-4 py-2 font-semibold", {
variants: {
intent: {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-200 text-black",
},
size: {
sm: "text-sm",
md: "text-base",
lg: "text-lg",
},
},
defaultVariants: {
intent: "primary",
size: "md",
},
});
export interface ButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "disabled">,
VariantProps<typeof button> {}
export const Button: React.FC<ButtonProps> = ({
className,
intent,
size,
...props
}) => <button className={button({ intent, size, className })} {...props} />;tsxเมื่อต้องการใช้งาน เราก็เพียงแค่เรียกผ่าน Props ดังนี้
<Button intent="primary">Save</Button>
<Button intent="secondary" size="sm">Cancel</Button>tsx☕ สรุปส่งท้าย#
จากตัวอย่างจะเห็นว่า Reusable Component ไม่เพียงแค่ประหยัดเวลาในการเขียนโค้ด แต่คือแนวคิดที่ทำให้ทีมทำงานอย่างเป็นระบบ มีความสอดคล้อง และบำรุงรักษาง่ายขึ้น โดยยึดหลักการสำคัญในการออกแบบที่ดี ซึ่งผลลัพธ์ปลายทางจะเห็นว่าโค้ดของโปรเจกต์สะอาดขึ้น และทีมจะสามารถทำงานได้อย่างมีประสิทธิภาพมากขึ้นอย่างแน่นอน 🥰