Devfavor

Back

สร้าง Component ครั้งเดียว ใช้ได้ทุกที่ – ลดความซ้ำซ้อน เพิ่มประสิทธิภาพงานพัฒนาBlur image

นักพัฒนาหลายคนอาจคุ้นเคยกับการเขียนโค้ด 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 ไม่เพียงแค่ประหยัดเวลาในการเขียนโค้ด แต่คือแนวคิดที่ทำให้ทีมทำงานอย่างเป็นระบบ มีความสอดคล้อง และบำรุงรักษาง่ายขึ้น โดยยึดหลักการสำคัญในการออกแบบที่ดี ซึ่งผลลัพธ์ปลายทางจะเห็นว่าโค้ดของโปรเจกต์สะอาดขึ้น และทีมจะสามารถทำงานได้อย่างมีประสิทธิภาพมากขึ้นอย่างแน่นอน 🥰

Code, coffee, and calm — the holy trinity of a good day. ☕

สร้าง Component ครั้งเดียว ใช้ได้ทุกที่ – ลดความซ้ำซ้อน เพิ่มประสิทธิภาพงานพัฒนา
Author Coffee Stack
Published at August 21, 2025