LearnVibeCode
Vibe Coding

Vibe Coding React: สร้าง Modern Web App ด้วย AI ใน 1 วัน

วิรุฬห์ เก่งธัญการ x Claude
#vibe-coding#react#nextjs#web-development#frontend

Vibe Coding React: สร้าง Modern Web App ด้วย AI ใน 1 วัน

React เป็น JavaScript library ที่ได้รับความนิยมสูงสุดสำหรับสร้าง web applications และเมื่อรวมกับ Vibe Coding คุณสามารถสร้าง production-ready apps ได้ในเวลาเพียงไ ม่กี่ชั่วโมง!

ทำไมต้อง React + Vibe Coding?

React ครองใจนักพัฒนา

ใช้โดยบริษัทระดับโลก

เตรียมตัวเริ่มต้น (15 นาที)

ติดตั้ง Node.js

# ตรวจสอบว่ามี Node.js แล้วหรือยัง
node --version
npm --version

# ถ้ายังไม่มี ดาวน์โหลดจาก nodejs.org
# แนะนำ LTS version

VS Code Extensions

1. ES7+ React/Redux/React-Native snippets
2. Prettier - Code formatter
3. ESLint
4. Auto Import
5. Tailwind CSS IntelliSense

สร้างโปรเจค React

# สร้าง React app ด้วย Vite (แนะนำ - เร็วกว่า)
npm create vite@latest my-app -- --template react

# หรือใช้ Create React App
npx create-react-app my-app

# เข้า folder
cd my-app

# ติดตั้ง dependencies
npm install

# รันโปรเจค
npm run dev

โปรเจค 1: Todo App แบบครบฟีเจอร์ (45 นาที)

สั่ง AI สร้าง Todo App

Prompt:

สร้าง React Todo App ที่มีฟีเจอร์:

Features:
1. เพิ่ม/แก้ไข/ลบ todo
2. ทำเครื่องหมายเสร็จ
3. Filter: All / Active / Completed
4. Counter แสดงจำนวน
5. Local Storage
6. Dark mode toggle

Design:
- ใช้ Tailwind CSS
- Responsive
- Smooth animations
- Modern UI

Tech Stack:
- React Hooks (useState, useEffect, useContext)
- Tailwind CSS
- React Icons

ให้โค้ดพร้อมคำอธิบาย

โค้ดที่ได้:

// App.jsx
import { useState, useEffect, createContext, useContext } from 'react';
import { FiSun, FiMoon, FiPlus, FiTrash2, FiEdit2, FiCheck } from 'react-icons/fi';

// Dark Mode Context
const DarkModeContext = createContext();

export default function App() {
  const [darkMode, setDarkMode] = useState(() => {
    const saved = localStorage.getItem('darkMode');
    return saved === 'true';
  });

  useEffect(() => {
    localStorage.setItem('darkMode', darkMode);
    document.documentElement.classList.toggle('dark', darkMode);
  }, [darkMode]);

  return (
    <DarkModeContext.Provider value={{ darkMode, setDarkMode }}>
      <div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
        <TodoApp />
      </div>
    </DarkModeContext.Provider>
  );
}

function TodoApp() {
  const { darkMode, setDarkMode } = useContext(DarkModeContext);
  const [todos, setTodos] = useState(() => {
    const saved = localStorage.getItem('todos');
    return saved ? JSON.parse(saved) : [];
  });
  const [filter, setFilter] = useState('all');
  const [inputValue, setInputValue] = useState('');
  const [editingId, setEditingId] = useState(null);

  // บันทึกลง localStorage
  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]);

  // เพิ่ม todo
  const addTodo = () => {
    if (!inputValue.trim()) return;

    if (editingId) {
      // แก้ไข todo
      setTodos(todos.map(todo =>
        todo.id === editingId
          ? { ...todo, text: inputValue }
          : todo
      ));
      setEditingId(null);
    } else {
      // เพิ่ม todo ใหม่
      const newTodo = {
        id: Date.now(),
        text: inputValue,
        completed: false,
        createdAt: new Date().toISOString()
      };
      setTodos([newTodo, ...todos]);
    }

    setInputValue('');
  };

  // Toggle completed
  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  };

  // ลบ todo
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  // เริ่มแก้ไข
  const startEdit = (todo) => {
    setEditingId(todo.id);
    setInputValue(todo.text);
  };

  // ยกเลิกแก้ไข
  const cancelEdit = () => {
    setEditingId(null);
    setInputValue('');
  };

  // Filter todos
  const filteredTodos = todos.filter(todo => {
    if (filter === 'active') return !todo.completed;
    if (filter === 'completed') return todo.completed;
    return true;
  });

  // นับจำนวน
  const activeCount = todos.filter(t => !t.completed).length;
  const completedCount = todos.filter(t => t.completed).length;

  return (
    <div className="container mx-auto px-4 py-8 max-w-2xl">
      {/* Header */}
      <div className="flex items-center justify-between mb-8">
        <h1 className="text-4xl font-bold text-gray-800 dark:text-white">
          ✨ My Tasks
        </h1>
        <button
          onClick={() => setDarkMode(!darkMode)}
          className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700
                   hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
        >
          {darkMode ? (
            <FiSun className="w-6 h-6 text-yellow-400" />
          ) : (
            <FiMoon className="w-6 h-6 text-gray-700" />
          )}
        </button>
      </div>

      {/* Stats */}
      <div className="grid grid-cols-3 gap-4 mb-6">
        <StatCard
          label="Total"
          value={todos.length}
          color="blue"
        />
        <StatCard
          label="Active"
          value={activeCount}
          color="yellow"
        />
        <StatCard
          label="Completed"
          value={completedCount}
          color="green"
        />
      </div>

      {/* Input */}
      <div className="mb-6">
        <div className="flex gap-2">
          <input
            type="text"
            value={inputValue}
            onChange={(e) => setInputValue(e.target.value)}
            onKeyPress={(e) => e.key === 'Enter' && addTodo()}
            placeholder={editingId ? "Edit task..." : "Add a new task..."}
            className="flex-1 px-4 py-3 rounded-lg border-2 border-gray-300
                     dark:border-gray-600 dark:bg-gray-800 dark:text-white
                     focus:border-blue-500 dark:focus:border-blue-400
                     outline-none transition-colors"
          />
          <button
            onClick={addTodo}
            className="px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white
                     rounded-lg font-medium transition-colors flex items-center gap-2"
          >
            {editingId ? (
              <>
                <FiCheck /> Update
              </>
            ) : (
              <>
                <FiPlus /> Add
              </>
            )}
          </button>
          {editingId && (
            <button
              onClick={cancelEdit}
              className="px-4 py-3 bg-gray-500 hover:bg-gray-600 text-white
                       rounded-lg transition-colors"
            >
              Cancel
            </button>
          )}
        </div>
      </div>

      {/* Filter */}
      <div className="flex gap-2 mb-6">
        {['all', 'active', 'completed'].map(f => (
          <button
            key={f}
            onClick={() => setFilter(f)}
            className={`px-4 py-2 rounded-lg font-medium capitalize transition-colors ${
              filter === f
                ? 'bg-blue-500 text-white'
                : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'
            }`}
          >
            {f}
          </button>
        ))}
      </div>

      {/* Todo List */}
      <div className="space-y-2">
        {filteredTodos.length === 0 ? (
          <div className="text-center py-12 text-gray-400 dark:text-gray-500">
            <p className="text-xl">No tasks {filter !== 'all' && `(${filter})`}</p>
            <p className="text-sm mt-2">Add a task to get started!</p>
          </div>
        ) : (
          filteredTodos.map(todo => (
            <TodoItem
              key={todo.id}
              todo={todo}
              onToggle={toggleTodo}
              onDelete={deleteTodo}
              onEdit={startEdit}
            />
          ))
        )}
      </div>
    </div>
  );
}

// Component: StatCard
function StatCard({ label, value, color }) {
  const colors = {
    blue: 'bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-300',
    yellow: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-600 dark:text-yellow-300',
    green: 'bg-green-100 dark:bg-green-900 text-green-600 dark:text-green-300'
  };

  return (
    <div className={`p-4 rounded-lg ${colors[color]}`}>
      <div className="text-2xl font-bold">{value}</div>
      <div className="text-sm opacity-75">{label}</div>
    </div>
  );
}

// Component: TodoItem
function TodoItem({ todo, onToggle, onDelete, onEdit }) {
  return (
    <div
      className={`group flex items-center gap-3 p-4 rounded-lg border-2
                ${todo.completed
                  ? 'bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600'
                  : 'bg-white dark:bg-gray-700 border-gray-200 dark:border-gray-600'
                }
                hover:shadow-md transition-all`}
    >
      {/* Checkbox */}
      <button
        onClick={() => onToggle(todo.id)}
        className={`flex-shrink-0 w-6 h-6 rounded border-2 flex items-center
                  justify-center transition-colors ${
          todo.completed
            ? 'bg-green-500 border-green-500'
            : 'border-gray-300 dark:border-gray-500 hover:border-green-500'
        }`}
      >
        {todo.completed && <FiCheck className="text-white" />}
      </button>

      {/* Text */}
      <span
        className={`flex-1 ${
          todo.completed
            ? 'line-through text-gray-400 dark:text-gray-500'
            : 'text-gray-800 dark:text-white'
        }`}
      >
        {todo.text}
      </span>

      {/* Actions */}
      <div className="flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
        <button
          onClick={() => onEdit(todo)}
          className="p-2 text-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900
                   rounded transition-colors"
        >
          <FiEdit2 />
        </button>
        <button
          onClick={() => onDelete(todo.id)}
          className="p-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-900
                   rounded transition-colors"
        >
          <FiTrash2 />
        </button>
      </div>
    </div>
  );
}

ติดตั้ง Dependencies

npm install react-icons
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configure Tailwind

// tailwind.config.js
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: 'class',
  theme: {
    extend: {},
  },
  plugins: [],
}
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

โปรเจค 2: E-commerce Product Page (60 นาที)

Prompt ให้ AI

Prompt:

สร้าง React E-commerce Product Page ที่มี:

Features:
1. Product image gallery (thumbnails + main image)
2. Product details (name, price, rating, description)
3. Color และ size selector
4. Quantity picker
5. Add to cart (with animation)
6. Related products section
7. Reviews section

Design:
- ใช้ Tailwind CSS
- Responsive (mobile-first)
- Smooth animations
- Modern, clean UI

State Management:
- ใช้ Context API สำหรับ Cart
- Custom hooks

ให้โค้ดครบพร้อม sample data

โปรเจค 3: Real-time Chat App (90 นาที)

สร้าง Chat App ด้วย Firebase

Prompt:

สร้าง React Real-time Chat App ด้วย Firebase:

Features:
1. User authentication (Google Sign-in)
2. Real-time messages
3. Online users indicator
4. Typing indicator
5. Image upload
6. Emoji picker
7. Message timestamp

Tech Stack:
- React + Hooks
- Firebase (Auth, Firestore, Storage)
- Tailwind CSS

ให้โค้ดครบพร้อม Firebase setup

Next.js: React Framework ระดับ Production

ทำไมต้อง Next.js?

สร้างโปรเจค Next.js

# สร้าง Next.js app
npx create-next-app@latest my-nextjs-app

# เลือก options:
# ✅ TypeScript? → Yes
# ✅ ESLint? → Yes
# ✅ Tailwind CSS? → Yes
# ✅ App Router? → Yes

cd my-nextjs-app
npm run dev

โปรเจค 4: Blog with Next.js (90 นาที)

Prompt ให้ AI

Prompt:

สร้าง Blog ด้วย Next.js App Router:

Features:
1. Homepage (list of posts)
2. Post detail page
3. Category filter
4. Search functionality
5. MDX support (markdown with components)
6. Table of contents
7. Reading time
8. Dark mode

Pages:
- app/page.tsx (homepage)
- app/blog/[slug]/page.tsx (post detail)
- app/category/[category]/page.tsx

Tech Stack:
- Next.js 14 (App Router)
- TypeScript
- Tailwind CSS
- next-mdx-remote
- gray-matter

ให้โค้ดครบพร้อม sample posts

React Patterns กับ Vibe Coding

1. Custom Hooks

Prompt:

สร้าง custom hooks สำหรับ:
1. useFetch - fetch data พร้อม loading/error states
2. useLocalStorage - sync state กับ localStorage
3. useDebounce - debounce value
4. useMediaQuery - responsive breakpoints
5. useOnClickOutside - detect click outside element

ให้โค้ดพร้อมตัวอย่างการใช้งาน

ตัวอย่าง:

// hooks/useFetch.js
import { useState, useEffect } from 'react';

export function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) throw new Error('Failed to fetch');
        const json = await response.json();
        setData(json);
        setError(null);
      } catch (err) {
        setError(err.message);
        setData(null);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

// การใช้งาน
function UserProfile({ userId }) {
  const { data, loading, error } = useFetch(`/api/users/${userId}`);

  if (loading) return <LoadingSpinner />;
  if (error) return <ErrorMessage message={error} />;

  return <div>{data.name}</div>;
}

2. Compound Components

Prompt:

สร้าง Tabs component แบบ Compound Pattern:
- Flexible API
- Accessible (keyboard navigation)
- Controlled/Uncontrolled modes
- Smooth animations

ใช้ React Context และ cloneElement

3. Render Props

Prompt:

สร้าง DataTable component ที่ใช้ Render Props:
- รองรับ sorting, filtering, pagination
- Customizable columns
- Loading states
- Empty states

ให้ตัวอย่างการใช้งานหลายแบบ

State Management

1. Context API + useReducer

Prompt:

สร้าง Shopping Cart ด้วย Context API และ useReducer:

Actions:
- ADD_ITEM
- REMOVE_ITEM
- UPDATE_QUANTITY
- CLEAR_CART
- APPLY_COUPON

Features:
- Calculate totals
- Apply discounts
- Persist to localStorage
- TypeScript types

ให้โค้ดครบพร้อม hooks สำหรับใช้งาน

2. Zustand (แนะนำ)

Prompt:

สร้าง Global State ด้วย Zustand สำหรับ:

Stores:
1. authStore (user, login, logout)
2. cartStore (items, addItem, removeItem)
3. uiStore (theme, sidebar, modals)

Features:
- Persistence
- DevTools
- TypeScript
- Middleware

ให้ตัวอย่างการใช้งานในหลาย components

Styling Strategies

1. Tailwind CSS (แนะนำ)

// Component-based
function Button({ children, variant = 'primary' }) {
  const baseClasses = 'px-4 py-2 rounded-lg font-medium transition-colors';
  const variants = {
    primary: 'bg-blue-500 hover:bg-blue-600 text-white',
    secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800',
    danger: 'bg-red-500 hover:bg-red-600 text-white'
  };

  return (
    <button className={`${baseClasses} ${variants[variant]}`}>
      {children}
    </button>
  );
}

2. CSS Modules

// Button.module.css
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
}

.primary {
  background: blue;
  color: white;
}

// Button.jsx
import styles from './Button.module.css';

function Button({ variant = 'primary' }) {
  return <button className={`${styles.button} ${styles[variant]}`} />;
}

3. Styled Components

Prompt:

สร้าง Design System ด้วย styled-components:

Components:
- Button (variants, sizes)
- Input, Textarea, Select
- Card, Modal, Dropdown
- Typography (H1-H6, Text)

Features:
- Theming
- Responsive
- TypeScript
- Storybook ready

Testing

Jest + React Testing Library

Prompt:

สร้าง unit tests สำหรับ:

1. TodoList component
   - render todos correctly
   - add new todo
   - toggle todo
   - delete todo

2. useFetch hook
   - loading state
   - success state
   - error state

3. Form validation
   - required fields
   - email validation
   - submit handler

ใช้ Jest + React Testing Library
ให้ best practices

Performance Optimization

1. React.memo

// ❌ ไม่ดี - re-render ทุกครั้ง
function ExpensiveComponent({ data }) {
  return <div>{/* complex rendering */}</div>;
}

// ✅ ดี - re-render เฉพาะเมื่อ data เปลี่ยน
const ExpensiveComponent = React.memo(function({ data }) {
  return <div>{/* complex rendering */}</div>;
});

2. useMemo & useCallback

function ProductList({ products, category }) {
  // ✅ Memoize ค่าที่ compute แพง
  const filteredProducts = useMemo(() => {
    return products.filter(p => p.category === category);
  }, [products, category]);

  // ✅ Memoize function
  const handleSort = useCallback((type) => {
    // sorting logic
  }, []);

  return <div>{/* render */}</div>;
}

3. Code Splitting

// Lazy loading
const AdminPanel = lazy(() => import('./AdminPanel'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <AdminPanel />
    </Suspense>
  );
}

เส้นทางเรียนรู้ React (8 สัปดาห์)

Week 1-2: React Basics

Week 3-4: Hooks & Advanced

Week 5-6: Ecosystem

Week 7-8: Next.js

สรุป

React + Vibe Coding เป็นทักษะที่คุ้มค่าที่สุดในตอนนี้:

อยากเป็น React Developer มือโปร? E-book Unlocked Vibe Coding มี:

📚 สั่งซื้อ E-book ที่นี่


ติดต่อเรียน React + AI:


อ่านบทความอื่นๆ:

← Back to Blog