HTML, CSS, JavaScript, React & Next.js 101
เรียนรู้พื้นฐานการพัฒนาเว็บ ตั้งแต่ HTML, CSS ไปจนถึง JavaScript, React และ Next.js
บทความนี้จะแนะนำพื้นฐานการพัฒนาเว็บ โดยเริ่มจาก HTML และ CSS สำหรับสร้างโครงสร้างและตกแต่งหน้าเว็บ จากนั้นต่อด้วย JavaScript กับ React/Next.js สำหรับสร้างเว็บแอปพลิเคชันแบบไดนามิกและโต้ตอบได้
ส่วนที่ 1: พื้นฐาน HTML#
HTML คืออะไร?#
HTML (HyperText Markup Language) เป็นภาษามาร์กอัปที่ใช้สร้างและออกแบบเว็บไซต์ โครงสร้างพื้นฐานของ HTML ประกอบด้วย elements ที่นำมารวมกันเพื่อสร้างหน้าเว็บ
เตรียมเครื่องมือ#
ข้อกำหนดเบื้องต้น: ติดตั้ง VS Code จาก code.visualstudio.com ↗
VS Code เป็น IDE (Integrated Development Environment) หรือเครื่องมือที่ใช้สำหรับเขียนโค้ด
การตั้งค่าโปรเจกต์#
# สร้างโฟลเดอร์สำหรับโปรเจกต์ HTML
mkdir html-css-tutorial
cd html-css-tutorial
# สร้างโฟลเดอร์สำหรับไฟล์ HTML
mkdir 01_html
cd 01_html
# เปิด VS Code ในโฟลเดอร์ปัจจุบัน
code .bashโครงสร้างพื้นฐานของ HTML#
ใน VS Code ให้สร้างไฟล์ชื่อ index.html พิมพ์ html:5 แล้วกด Enter/Tab เพื่อสร้างโครงสร้าง HTML เริ่มต้น:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
</head>
<body>
</body>
</html>htmlองค์ประกอบพื้นฐานของ HTML#
| Element | คำอธิบาย |
|---|---|
<!DOCTYPE html> | ประกาศว่าเอกสารนี้เป็น HTML5 |
<html> | รากของเอกสาร HTML |
<head> | บรรจุข้อมูลเมตาดาต้า (ไม่แสดงบนหน้าเว็บ) |
<title> | กำหนดชื่อหน้าเว็บ แสดงที่แถบชื่อเบราว์เซอร์ |
<body> | บรรจุเนื้อหาที่แสดงบนหน้าเว็บ |
<h1> - <h6> | หัวข้อขนาดต่างๆ (h1 ใหญ่สุด) |
<p> | ย่อหน้าข้อความ |
<a href="URL"> | ลิงก์ไปยังหน้าเว็บอื่น |
<img src="..." alt="..."> | แทรกรูปภาพ |
หัวข้อและย่อหน้า#
เพิ่มเนื้อหาภายใน <body>:
<body>
<h1>เว็บไซต์ กานต์ ยงศิริวุฒิ</h1>
<p>ยินดีต้อนรับสู่เว็บไซต์ของเรา</p>
<h2>ข่าวสาร</h2>
<p>อ่านข่าวสารจากเว็บไซต์ของเรา</p>
<h2>กิจกรรม</h2>
<p>เรามีกิจกรรมมากมาย</p>
</body>html
ลิงก์ (Anchor Tag)#
สร้างไฟล์ชื่อ about.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About</title>
</head>
<body>
<h1>เกี่ยวกับฉัน</h1>
</body>
</html>htmlใน index.html เพิ่มลิงก์สำหรับหน้าเว็บภายในและภายนอก:
<!-- ลิงก์ภายใน -->
<a href="about.html">เกี่ยวกับเรา</a>
<!-- ลิงก์ภายนอก -->
<a href="https://melivecode.com">เยี่ยมชม MeLiveCode</a>htmlรูปภาพ#
แท็ก <img> ใช้แสดงรูปภาพโดยระบุตำแหน่งไฟล์ (src) และข้อความทางเลือก (alt):
<!-- รูปภาพในเครื่อง -->
<img src="cat_meme.jpg" alt="แมวประหลาด" width="100px" height="150px" />
<!-- รูปภาพจากอินเทอร์เน็ต -->
<img src="https://melivecode.com/img/logo.png" alt="หมีกำลังเขียนโค้ด" height="150px" />html
รายการ (Lists)#
HTML รองรับทั้งรายการแบบไม่มีลำดับ (bullet points) และรายการแบบมีลำดับ (ตัวเลข):
<!-- รายการไม่มีลำดับ -->
<ul>
<li>ดิท มีครูใหม่</li>
<li>ฝนตกหนักที่อาร์เอสยู</li>
<li>ตลาดอาร์เอสยู หน้าอาคาร 6</li>
</ul>
<!-- รายการมีลำดับ -->
<ol>
<li>รับน้อง</li>
<li>วันหยุดชาติ</li>
<li>ปิดเทอม</li>
</ol>html| แท็ก | คำอธิบาย |
|---|---|
<ul> | รายการไม่มีลำดับ (bullet points) |
<ol> | รายการมีลำดับ (ตัวเลข) |
<li> | รายการย่อย |
แบบฟอร์ม (Forms)#
แท็ก <form> ใช้รับข้อมูลจากผู้ใช้และส่งข้อมูลไปยังเซิร์ฟเวอร์
สร้างไฟล์ชื่อ login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<h1>เข้าสู่ระบบ</h1>
<form action="login.php" method="POST">
<label for="username">ชื่อผู้ใช้:</label>
<input type="text" id="username" name="username" />
<br />
<label for="password">รหัสผ่าน:</label>
<input type="password" id="password" name="password" />
<br />
<input type="checkbox" id="remember" name="remember" />
<label for="remember">จดจำฉัน</label>
<br/>
<input type="submit" value="เข้าสู่ระบบ" />
</form>
</body>
</html>html
องค์ประกอบของแบบฟอร์ม#
| Element | วัตถุประสงค์ |
|---|---|
<form action=""> | กำหนดขอบเขตสำหรับรับข้อมูล; action ระบุตำแหน่งที่ส่งข้อมูล |
<label for="id"> | กำหนดชื่อให้ input; ช่วยเรื่อง accessibility |
<input> | ประเภท input ต่างๆ (text, password, checkbox, submit) |
id attribute | เชื่อม input กับ label |
name attribute | ชื่อตัวแปร/พารามิเตอร์สำหรับส่งข้อมูล |
หมายเหตุ: สำหรับ HTML elements และตัวอย่างเพิ่มเติม สามารถดูได้ที่ W3Schools HTML ↗
ส่วนที่ 2: พื้นฐาน CSS#
การตั้งค่าโปรเจกต์#
# สร้างโฟลเดอร์สำหรับไฟล์ CSS
mkdir 02_css
cd 02_css
# เปิด VS Code ในโฟลเดอร์ปัจจุบัน
code .bashใน VS Code สร้างไฟล์ชื่อ index.html พิมพ์ html:5 แล้วกด Enter/Tab เพื่อสร้างโครงสร้าง HTML เริ่มต้น:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Practice</title>
</head>
<body>
</body>
</html>htmlCSS คืออะไร?#
CSS (Cascading Style Sheet) เป็นภาษาที่ใช้กำหนดรูปแบบและสไตล์ (รูปลักษณ์) ของหน้าเว็บ เช่น สีพื้นหลัง ตำแหน่งองค์ประกอบ ฟอนต์ และอื่นๆ
Inline CSS#
สามารถเพิ่มสไตล์โดยตรงใน element โดยใช้ attribute style:
<body style="background-color: beige;">
<h1 style="color: darkblue;">Hello World</h1>
</body>htmlรูปแบบ CSS#
CSS rules มีโครงสร้างดังนี้:
selector {
property: value;
property: value;
}css| ส่วน | คำอธิบาย |
|---|---|
selector | เลือก HTML elements ที่จะใช้สไตล์ (element, class, หรือ id) |
property | คุณสมบัติที่ต้องการกำหนด (background-color, color, font-size, margin, padding) |
value | ค่าของคุณสมบัตินั้น (aqua, #FF0000, 16px) |
Internal CSS (Style Element)#
แท็ก <style> ใช้กำหนด CSS ภายในไฟล์ HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Styled Page</title>
<style>
body {
background-color: beige;
}
h1 {
color: darkblue;
font-size: 32px;
}
</style>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>htmlExternal CSS (แนะนำ)#
สร้างไฟล์ CSS แยกและเชื่อมโยงใน HTML:
ไฟล์: style.css
body {
background-color: beige;
margin: 0;
}
h1 {
color: darkblue;
}cssไฟล์: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Styled Page</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello World</h1>
</body>
</html>htmlการแยก CSS ไว้ในไฟล์ภายนอกเป็นวิธีที่นิยมใช้มากที่สุดเพราะทำให้จัดการสไตล์ได้ง่ายขึ้น
หมายเหตุ: สำหรับอ้างอิง CSS properties สามารถดูได้ที่ W3Schools CSS Reference ↗
ส่วนเสริม Live Server#
ติดตั้งส่วนเสริม Five Server ใน VS Code เพื่อให้เบราว์เซอร์โหลดอัตโนมัติเมื่อมีการเปลี่ยนแปลงโค้ด:
- ไปที่ Extensions
- ค้นหา “Five Server”
- ติดตั้งส่วนเสริม
- คลิกขวาที่
index.htmlและเลือก “Open with Five Server”
CSS Classes และ Div Elements#
Class ใน CSS ใช้กำหนดสไตล์ที่สามารถนำไปใช้กับหลาย elements
ไฟล์: style.css
body {
background-color: beige;
}
.box {
border: 2px solid brown;
width: 200px;
padding: 10px;
margin: 10px;
}cssไฟล์: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Classes</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="box">
<h1>กล่องที่ 1</h1>
<p>นี่คือเนื้อหาในกล่องที่ 1</p>
</div>
<div class="box">
<h1>กล่องที่ 2</h1>
<p>นี่คือเนื้อหาในกล่องที่ 2</p>
</div>
</body>
</html>html| Concept | คำอธิบาย |
|---|---|
<div> | คอนเทนเนอร์ทั่วไปสำหรับรวม elements เข้าด้วยกัน |
.classname | CSS class selector (เริ่มต้นด้วยจุด) |
#idname | CSS ID selector (เริ่มต้นด้วยเครื่องหมาย #) |
แถบนำทาง (Navigation Bar)#
สร้างแถบนำทางโดยใช้ <ul> และ <li>:
ไฟล์: style.css
body {
background-color: beige;
margin: 0;
}
.navMenu {
list-style-type: none;
margin: 0;
padding: 0;
background-color: darkgreen;
overflow: hidden;
}
.navItem {
float: left;
}
.navItem a {
color: white;
text-decoration: none;
display: block;
padding: 14px 16px;
text-align: center;
}
.navItem a:hover {
background-color: #115511;
}cssไฟล์: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Navigation Bar</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<ul class="navMenu">
<li class="navItem"><a href="index.html">หน้าแรก</a></li>
<li class="navItem"><a href="about.html">เกี่ยวกับเรา</a></li>
<li class="navItem"><a href="contact.html">ติดต่อ</a></li>
</ul>
<h1>ยินดีต้อนรับ</h1>
</body>
</html>htmlคำอธิบายคุณสมบัติ CSS#
| Property | Value | คำอธิบาย |
|---|---|---|
list-style-type | none | ลบ bullet points ออกจากรายการ |
margin | 0 | ลบระยะห่างภายนอก |
padding | 0 | ลบระยะห่างภายใน |
background-color | darkgreen | กำหนดสีพื้นหลัง |
overflow | hidden | ครอบ elements ที่ลอยอยู่ภายใน parent |
float | left | วางรายการแนวนอน |
text-decoration | none | ลบเส้นใต้ลิงก์ |
display | block | ทำให้ทั้งพื้นที่คลิกได้ |
Flexbox Layout#
Flexbox ใช้จัดเรียงเนื้อหาแบบยืดหยุ่นสำหรับ responsive layouts:
ไฟล์: style.css
body {
background-color: beige;
margin: 0;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 20px;
}
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 10px;
text-align: center;
margin: 10px;
}
.card img {
width: 100%;
object-fit: cover;
border-radius: 4px;
}cssไฟล์: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox Cards</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>สถานที่ท่องเที่ยวยอดนิยม</h1>
<div class="container">
<div class="card">
<img src="https://www.melivecode.com/attractions/1.jpg" alt="หมู่เกาะพีพี" />
<h2>หมู่เกาะพีพี</h2>
</div>
<div class="card">
<img src="https://www.melivecode.com/attractions/2.jpg" alt="หอไอเฟล" />
<h2>หอไอเฟล</h2>
</div>
<div class="card">
<img src="https://www.melivecode.com/attractions/3.jpg" alt="ไทม์สแควร์" />
<h2>ไทม์สแควร์</h2>
</div>
<div class="card">
<img src="https://www.melivecode.com/attractions/4.jpg" alt="ภูเขาไฟฟูจิ" />
<h2>ภูเขาไฟฟูจิ</h2>
</div>
</div>
</body>
</html>htmlคุณสมบัติ Flexbox#
| Property | คำอธิบาย |
|---|---|
display: flex | ทำให้ element เป็น flex container |
flex-wrap: wrap | อนุญาตให้ items ขึ้นบรรทัดใหม่ |
justify-content: center | จัด items กึ่งกลางแนวนอน |
gap | เพิ่มระยะห่างระหว่าง flex items |
หมายเหตุ: สำหรับคำอธิบาย Flexbox อย่างละเอียด ดูได้ที่ CSS-Tricks Flexbox Guide ↗
ปุ่มลัดที่มีประโยชน์#
| Action | Windows/Linux | macOS |
|---|---|---|
| คัดลอก | Ctrl + C | Cmd + C |
| วาง | Ctrl + V | Cmd + V |
| บันทึก | Ctrl + S | Cmd + S |
| ตัด | Ctrl + X | Cmd + X |
| เลิกทำ | Ctrl + Z | Cmd + Z |
| ทำซ้ำ | Ctrl + Y | Cmd + Y |
ส่วนที่ 3: JavaScript กับ Next.js#
Next.js คืออะไร?#
- React.js เป็นไลบรารี JavaScript สำหรับสร้างส่วนติดต่อผู้ใช้ (Frontend)
- Next.js เป็นเฟรมเวิร์กที่สร้างขึ้นบน React.js ทำให้สร้างเว็บแอปพลิเคชันได้สะดวกขึ้น
- Node.js เป็นสภาพแวดล้อม JavaScript ที่ให้ JavaScript ทำงานนอกเบราว์เซอร์ได้
การติดตั้ง Next.js#
ข้อกำหนดเบื้องต้น: ติดตั้ง Node.js (LTS) จาก nodejs.org ↗
การตั้งค่าโปรเจกต์#
# สร้างโปรเจกต์ Next.js ใหม่
npx create-next-app@15 nextjs-tutorial
# เลือกตัวเลือกเมื่อถูกถาม:
# - Project name: nextjs-tutorial (หรือชื่อที่ต้องการ)
# - TypeScript: No
# - ESLint: Yes
# - Tailwind CSS: No (สำหรับเรียนรู้พื้นฐาน)
# - App Router: Yes
# - Customize default import alias: No
# ไปที่โปรเจกต์
cd nextjs-tutorial
# เปิด VS Code ในโฟลเดอร์ปัจจุบัน
code .
# เปิด Terminal ใน VS Code และเริ่ม development server
npm run devbashเปิดเบราว์เซอร์และไปที่ http://localhost:3000 เพื่อดูแอปพลิเคชัน
ส่วนเสริม VS Code ที่แนะนำ#
ติดตั้งส่วนเสริม ES7+ React/Redux/React-Native snippets สำหรับรองรับ React ใน VS Code
โครงสร้าง React Component#
ใน Next.js (App Router) หน้าเว็บถูกสร้างเป็น React components ในโฟลเดอร์ app
ไฟล์: app/page.js
export default function Page() {
return (
<div>
<div className='box'>
<h1>Hello World, Next.js</h1>
<p>ยินดีต้อนรับสู่ React!</p>
</div>
</div>
)
}javascriptแนวคิดสำคัญ#
| Concept | คำอธิบาย |
|---|---|
export default | ส่งออก component ให้สามารถนำเข้า/ใช้ได้ |
function Component() | การประกาศ Functional Component |
return (...) | ส่งค่า JSX (UI markup) กลับ |
className | React ใช้ className แทน class |
| JSX | JavaScript + XML — ไวยากรณ์คล้าย HTML ใน JavaScript |
หมายเหตุ: ใน React/Next.js ให้ใช้
classNameแทนclassสำหรับกำหนดสไตล์ element
การเพิ่ม CSS#
ไฟล์: app/style.css
.box {
border: 2px solid brown;
width: 200px;
padding: 10px;
margin: 10px;
background-color: beige;
}cssไฟล์: app/layout.js
import './style.css'
export const metadata = {
title: 'Next.js Tutorial',
description: 'Learn JavaScript with Next.js',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}javascriptพื้นฐาน JavaScript ใน React/Next.js#
1. ตัวแปร (Variables)#
สร้างไฟล์ app/01_var/page.js — เข้าถึงที่ http://localhost:3000/01_var
export default function VarPage() {
const name = "Karn";
let age = 25;
const isStudent = true;
return (
<div>
<h1>ตัวแปร</h1>
<p>ชื่อ: {name}</p>
<p>อายุ: {age}</p>
<p>เป็นนักเรียน: {isStudent ? "ใช่" : "ไม่ใช่"}</p>
</div>
);
}javascript| Keyword | การใช้งาน |
|---|---|
var | ไม่ควรใช้ — มีปัญหาเรื่อง scope |
let | ใช้สำหรับตัวแปรที่ค่าเปลี่ยนแปลงได้ |
const | ใช้สำหรับตัวแปรที่ค่าไม่เปลี่ยนแปลง |
2. อาร์เรย์ (Arrays)#
สร้างไฟล์ app/02_array/page.js — เข้าถึงที่ http://localhost:3000/02_array
export default function ArrayPage() {
const fruits = ["ส้ม", "กล้วย", "มะม่วง", "สตรอว์เบอร์รี"];
return (
<div>
<h1>อาร์เรย์</h1>
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
</div>
);
}javascriptหมายเหตุ: prop
keyจำเป็นสำหรับรายการเพื่อให้ React ติดตามว่ารายการใดเปลี่ยนแปลง
3. ออบเจกต์ (Objects)#
สร้างไฟล์ app/03_object/page.js — เข้าถึงที่ http://localhost:3000/03_object
export default function ObjectPage() {
const user = {
name: "กานต์ ยงศิริวุฒิ",
role: "นักพัฒนา",
country: "ไทย"
};
return (
<div>
<h1>ออบเจกต์</h1>
<p>ชื่อ: {user.name}</p>
<p>ตำแหน่ง: {user.role}</p>
<p>ประเทศ: {user.country}</p>
</div>
);
}javascriptออบเจกต์เก็บข้อมูลเป็นคู่ key-value ใช้ dot notation (user.name) สำหรับเข้าถึงคุณสมบัติ
4. อาร์เรย์ของออบเจกต์ (Array of Objects)#
สร้างไฟล์ app/04_array_objects/page.js — เข้าถึงที่ http://localhost:3000/04_array_objects
export default function ArrayObjectsPage() {
const attractions = [
{ id: 1, name: "หมู่เกาะพีพี", country: "ไทย" },
{ id: 2, name: "หอไอเฟล", country: "ฝรั่งเศส" },
{ id: 3, name: "ไทม์สแควร์", country: "อเมริกา" },
{ id: 4, name: "ภูเขาไฟฟูจิ", country: "ญี่ปุ่น" }
];
return (
<div>
<h1>อาร์เรย์ของออบเจกต์</h1>
{attractions.map((attraction) => (
<div key={attraction.id} style={{ border: "1px solid #ccc", margin: "10px", padding: "10px" }}>
<h2>{attraction.name}</h2>
<p>ประเทศ: {attraction.country}</p>
</div>
))}
</div>
);
}javascriptอาร์เรย์ของออบเจกต์เป็นวิธีที่พบบ่อยที่สุดในการแสดงข้อมูลบนหน้าเว็บ ใช้ map() เพื่อวนลูปและแสดงแต่ละรายการ
5. ฟังก์ชัน (Functions)#
สร้างไฟล์ app/05_function/page.js — เข้าถึงที่ http://localhost:3000/05_function
export default function FunctionPage() {
// ฟังก์ชันบวกเลขสองตัว
function add(a, b) {
return a + b;
}
// Arrow function คูณเลขสองตัว
const multiply = (a, b) => {
return a * b;
};
// Arrow function แบบสั้น (implicit return)
const subtract = (a, b) => a - b;
// ฟังก์ชันตรวจสอบว่าเป็นผู้ใหญ่หรือไม่
const isAdult = (age) => {
return age >= 18;
};
// ฟังก์ชันสร้างคำทักทาย
const greet = (name, timeOfDay) => {
return `สวัสดี${timeOfDay}, ${name}!`;
};
return (
<div>
<h1>ฟังก์ชัน</h1>
<h2>การคำนวณพื้นฐาน</h2>
<p>5 + 3 = {add(5, 3)}</p>
<p>4 × 6 = {multiply(4, 6)}</p>
<p>10 - 4 = {subtract(10, 4)}</p>
<h2>ตรรกะแบบมีเงื่อนไข</h2>
<p>อายุ 20 เป็นผู้ใหญ่: {isAdult(20) ? "ใช่" : "ไม่ใช่"}</p>
<p>อายุ 15 เป็นผู้ใหญ่: {isAdult(15) ? "ใช่" : "ไม่ใช่"}</p>
<h2>เทมเพลตสตริง</h2>
<p>{greet("Karn", "ตอนเช้า")}</p>
<p>{greet("Somchai", "ตอนเย็น")}</p>
</div>
);
}javascriptรูปแบบฟังก์ชัน#
| Type | Syntax | Use Case |
|---|---|---|
| Function Declaration | function name() {} | ใช้ทั่วไป เรียกได้ก่อนประกาศ |
| Function Expression | const name = function() {} | เมื่อกำหนดให้ตัวแปร |
| Arrow Function | const name = () => {} | สั้น ทันสมัย ใช้บ่อยใน React |
| Arrow (Implicit Return) | const name = () => value | ฟังก์ชันบรรทัดเดียว ไม่ต้องมี return |
การตั้งชื่อฟังก์ชัน#
| Convention | Rule | Valid Examples | Invalid |
|---|---|---|---|
| camelCase | คำแรกพิมพ์เล็ก คำต่อไปพิมพ์ใหญ่ | getUserData, calculateTotal, isValid | GetUserData, get_user_data |
| Verb-first | เริ่มด้วยคำกริยาที่อธิบายการกระทำ | fetchUser, saveData, deleteItem | user, data, item |
| Booleans | ใช้คำนำหน้า is, has, can, should | isLoggedIn, hasPermission, canEdit | logged, permission, editable |
การใช้ฟังก์ชันกับ map()#
ฟังก์ชันมีประโยชน์มากเมื่อทำงานกับอาร์เรย์ สามารถส่งฟังก์ชันไปยัง map():
export default function FunctionMapPage() {
const products = [
{ id: 1, name: "แล็ปท็อป", price: 25000 },
{ id: 2, name: "เมาส์", price: 500 },
{ id: 3, name: "คีย์บอร์ด", price: 1500 }
];
// ฟังก์ชันคำนวณราคาหลังหักส่วนลด 10%
const calculateDiscount = (price) => {
return price * 0.9;
};
// ฟังก์ชันจัดรูปแบบสกุลเงิน
const formatCurrency = (amount) => {
return `฿${amount.toLocaleString()}`;
};
return (
<div>
<h1>ฟังก์ชันกับอาร์เรย์</h1>
<h2>ราคาสินค้า (หลังหักส่วนลด 10%)</h2>
{products.map((product) => (
<div key={product.id} style={{ border: "1px solid #ccc", margin: "10px", padding: "10px" }}>
<h3>{product.name}</h3>
<p>ราคาเดิม: {formatCurrency(product.price)}</p>
<p>ราคาหลังหักส่วนลด: {formatCurrency(calculateDiscount(product.price))}</p>
</div>
))}
</div>
);
}javascriptเคล็ดลับ: ฟังก์ชันช่วยให้นำโค้ดกลับมาใช้ใหม่ได้และทำให้ components เป็นระเบียบ แบ่งตรรกะที่ซับซ้อนออกเป็นฟังก์ชันเล็กๆ ที่เน้นเฉพาะหน้าที่
สารบัญ#
อัปเดต app/page.js เพื่อสร้างลิงก์ไปยังหน้าทั้งหมด:
import Link from 'next/link'
export default function Home() {
return (
<div style={{ padding: '20px' }}>
<h1>บทเรียน JavaScript</h1>
<ul>
<li><Link href="/01_var">ตัวแปร</Link></li>
<li><Link href="/02_array">อาร์เรย์</Link></li>
<li><Link href="/03_object">ออบเจกต์</Link></li>
<li><Link href="/04_array_objects">อาร์เรย์ของออบเจกต์</Link></li>
<li><Link href="/05_function">ฟังก์ชัน</Link></li>
</ul>
</div>
)
}javascriptส่วนที่ 4: พื้นฐาน React (Interactive Components)#
State ของ React คืออะไร?#
State คือข้อมูลที่สามารถเปลี่ยนแปลงได้ตามเวลาใน component ของคุณ เมื่อ state เปลี่ยน React จะอัปเดต UI โดยอัตโนมัติเพื่อสะท้อนการเปลี่ยนแปลงเหล่านั้น นี่คือสิ่งที่ทำให้ React เป็น “reactive” — UI ตอบสนองต่อการเปลี่ยนแปลงข้อมูล
1. useState Hook (Show/Hide & Toggle)#
Hook useState ให้คุณเพิ่ม state ใน functional components ใช้บ่อยสำหรับ toggle UI elements เช่น accordions, modals, และ like buttons
สร้างไฟล์ app/06_show_hide/page.js — เข้าถึงที่ http://localhost:3000/06_show_hide
'use client'
import { useState } from 'react'
export default function ShowHidePage() {
const [isVisible, setIsVisible] = useState(false)
const [faqOpen, setFaqOpen] = useState(null)
const [liked, setLiked] = useState(false)
const faqs = [
{ id: 1, question: 'React คืออะไร?', answer: 'React เป็นไลบรารี JavaScript สำหรับสร้างส่วนติดต่อผู้ใช้' },
{ id: 2, question: 'Next.js คืออะไร?', answer: 'Next.js เป็นเฟรมเวิร์กที่สร้างขึ้นบน React' },
{ id: 3, question: 'useState คืออะไร?', answer: 'useState เป็น React hook สำหรับจัดการ state ของ component' },
]
return (
<div style={{ padding: '20px' }}>
<h1>ตัวอย่าง Show/Hide & Toggle</h1>
{/* ตัวอย่างที่ 1: ปุ่ม Show/Hide */}
<section style={{ marginBottom: '30px' }}>
<h2>1. Show/Hide Content</h2>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? 'ซ่อน' : 'แสดง'} รายละเอียด
</button>
{isVisible && (
<p style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
เนื้อหานี้ถูกซ่อนไว้โดย default และจะแสดงเมื่อคุณคลิกปุ่ม
</p>
)}
</section>
{/* ตัวอย่างที่ 2: FAQ Accordion */}
<section style={{ marginBottom: '30px' }}>
<h2>2. FAQ Accordion</h2>
{faqs.map((faq) => (
<div key={faq.id} style={{ border: '1px solid #ddd', marginBottom: '10px' }}>
<button
onClick={() => setFaqOpen(faqOpen === faq.id ? null : faq.id)}
style={{ width: '100%', textAlign: 'left', padding: '10px' }}
>
{faq.question} {faqOpen === faq.id ? '▲' : '▼'}
</button>
{faqOpen === faq.id && (
<p style={{ padding: '10px' }}>{faq.answer}</p>
)}
</div>
))}
</section>
{/* ตัวอย่างที่ 3: Like Button */}
<section>
<h2>3. Like/Favorite Button</h2>
<button
onClick={() => setLiked(!liked)}
style={{
padding: '10px 20px',
backgroundColor: liked ? 'red' : '#ddd',
color: liked ? 'white' : 'black',
border: 'none',
borderRadius: '20px',
cursor: 'pointer'
}}
>
{liked ? '❤️ ถูกใจแล้ว' : '🤍 กดถูกใจ'}
</button>
{liked && <p style={{ marginTop: '10px' }}>ขอบคุณที่กดถูกใจ!</p>}
</section>
</div>
)
}javascriptแนวคิด useState#
| Concept | คำอธิบาย |
|---|---|
'use client' | Directive ที่จำเป็นสำหรับ components ที่ใช้ hooks (useState, useEffect) |
useState(initialValue) | สร้าง state ด้วยค่าเริ่มต้น |
[state, setState] | Array destructuring: ค่าปัจจุบันและฟังก์ชันอัปเดต |
!isVisible | NOT operator — สลับค่า boolean |
isVisible && ... | Conditional rendering — แสดงเฉพาะถ้าเป็น true |
2. Event Handling & Controlled Components#
React ใช้ synthetic events ที่ทำงานสม่ำเสมอในทุกเบราว์เซอร์ Events ใช้ชื่อแบบ camelCase (onClick, onChange) ไม่ใช่ตัวพิมพ์เล็ก
สร้างไฟล์ app/07_forms/page.js — เข้าถึงที่ http://localhost:3000/07_forms
'use client'
import { useState } from 'react'
export default function FormsPage() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [category, setCategory] = useState('electronics')
const [message, setMessage] = useState('')
const handleSubmit = (e) => {
e.preventDefault() // ป้องกันการ reload หน้า
setMessage(`ขอบคุณ ${name}! เราจะติดต่อคุณที่ ${email}`)
}
return (
<div style={{ padding: '20px' }}>
<h1>ตัวอย่างแบบฟอร์ม</h1>
<form onSubmit={handleSubmit} style={{ maxWidth: '400px' }}>
{/* Text Input */}
<div style={{ marginBottom: '15px' }}>
<label htmlFor="name">ชื่อ:</label>
<input
id="name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="กรอกชื่อของคุณ"
style={{ marginLeft: '10px', padding: '5px' }}
/>
</div>
{/* Email Input */}
<div style={{ marginBottom: '15px' }}>
<label htmlFor="email">อีเมล:</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="your@email.com"
style={{ marginLeft: '10px', padding: '5px' }}
/>
</div>
{/* Select Dropdown */}
<div style={{ marginBottom: '15px' }}>
<label htmlFor="category">หมวดหมู่:</label>
<select
id="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
style={{ marginLeft: '10px', padding: '5px' }}
>
<option value="electronics">อิเล็กทรอนิกส์</option>
<option value="clothing">เสื้อผ้า</option>
<option value="books">หนังสือ</option>
<option value="other">อื่นๆ</option>
</select>
</div>
{/* Submit Button */}
<button type="submit" style={{ padding: '10px 20px' }}>
ส่งข้อมูล
</button>
</form>
{/* Form Preview */}
{name && (
<div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#f0f0f0' }}>
<h3>ตัวอย่าง:</h3>
<p>ชื่อ: {name}</p>
<p>อีเมล: {email}</p>
<p>หมวดหมู่: {category}</p>
</div>
)}
{/* Success Message */}
{message && (
<p style={{ marginTop: '20px', color: 'green', fontWeight: 'bold' }}>
{message}
</p>
)}
</div>
)
}javascriptแนวคิด Event Handling#
| Concept | คำอธิบาย |
|---|---|
onClick | ทำงานเมื่อ element ถูกคลิก |
onChange | ทำงานเมื่อค่า input เปลี่ยน |
onSubmit | ทำงานเมื่อฟอร์มถูกส่ง |
e.preventDefault() | ป้องกันการกระทำ default (reload หน้า) |
e.target.value | รับค่าปัจจุบันจาก input |
value={state} | Controlled component — ค่าผูกกับ state |
htmlFor | React เทียบเท่า for attribute (สำหรับ labels) |
3. Conditional Rendering#
React มีหลายวิธีในการแสดงผลแบบมีเงื่อนไขตาม state หรือ props
สร้างไฟล์ app/08_conditional/page.js — เข้าถึงที่ http://localhost:3000/08_conditional
'use client'
import { useState } from 'react'
export default function ConditionalPage() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const user = {
name: 'Karn Yongsiriwut',
email: 'karn@example.com'
}
return (
<div style={{ padding: '20px' }}>
<h1>Conditional Rendering</h1>
{/* Toggle Login */}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Logout' : 'Login'}
</button>
{/* Ternary Operator - Either A or B */}
{!isLoggedIn ? (
<p style={{ marginTop: '20px' }}>Please log in to continue</p>
) : (
<p style={{ marginTop: '20px' }}>Welcome back, {user.name}!</p>
)}
{/* AND Operator - Shows only if true */}
{isLoggedIn && (
<div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#e8f5e9' }}>
<p>You are now logged in!</p>
<p>Email: {user.email}</p>
</div>
)}
</div>
)
}javascriptวิธี Conditional Rendering#
| Method | Syntax | Use Case |
|---|---|---|
| Ternary | {condition ? A : B} | เมื่อต้องการแสดง A หรือ B |
| AND operator | {condition && A} | เมื่อต้องการแสดง A เฉพาะถ้า true |
4. พื้นฐาน React Components#
Components เป็นอิฐมอซที่ใช้ซ้ำได้ใน React คุณสามารถสร้าง components ของคุณเองและส่งข้อมูลไปยังพวกมันโดยใช้ props (properties)
ก่อนอื่น สร้าง Card component ที่ใช้ซ้ำได้:
ไฟล์: components/Card.js
export default function Card({ title, description, image, color }) {
return (
<div style={{
border: '1px solid #ddd',
borderRadius: '8px',
padding: '15px',
margin: '10px',
backgroundColor: color || 'white',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
{image && (
<img
src={image}
alt={title}
style={{ width: '100%', height: '150px', objectFit: 'cover', borderRadius: '4px' }}
/>
)}
<h3 style={{ margin: '10px 0' }}>{title}</h3>
<p style={{ color: '#666' }}>{description}</p>
</div>
)
}javascriptตอนนี้ใช้ Card component:
ไฟล์: app/09_components/page.js — เข้าถึงที่ http://localhost:3000/09_components
import Card from "@/components/Card"
export default function ComponentsPage() {
return (
<div style={{ padding: '20px' }}>
<h1>Components ที่ใช้ซ้ำได้</h1>
<p>Components สามารถใช้ซ้ำด้วย props ที่แตกต่างกัน</p>
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<Card
title="React"
description="ไลบรารี JavaScript สำหรับสร้างส่วนติดต่อผู้ใช้"
color="#e3f2fd"
/>
<Card
title="Next.js"
description="เฟรมเวิร์ก React สำหรับแอปพลิเคชันที่พร้อมใช้งานจริง"
color="#f3e5f5"
/>
<Card
title="Node.js"
description="JavaScript runtime สำหรับสร้างแอปฝั่งเซิร์ฟเวอร์"
color="#e8f5e9"
/>
</div>
<h2>พร้อมรูปภาพ</h2>
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<Card
title="หมู่เกาะพีพี"
description="เกาะสวยในประเทศไทย"
image="https://www.melivecode.com/attractions/1.jpg"
/>
<Card
title="หอไอเฟล"
description="สถานที่สำคัญในปารีส ประเทศฝรั่งเศส"
image="https://www.melivecode.com/attractions/2.jpg"
/>
</div>
</div>
)
}javascriptแนวคิด Components#
| Concept | คำอธิบาย |
|---|---|
| Component | ส่วนของ UI ที่ใช้ซ้ำได้ |
| Props | ข้อมูลที่ส่งไปยัง components (อ่านได้อย่างเดียว) |
| Export | ทำให้ component สามารถนำเข้า/ใช้ได้ |
| Import | นำ component เข้ามาในไฟล์อื่น |
{title} | Destructuring props ใน parameters ของฟังก์ชัน |
| `{color |
5. List Rendering กับ Components#
การแสดงรายการเป็น pattern ที่พบบ่อยที่สุดใน React ใช้ .map() เพื่อแปลง arrays เป็น JSX elements
สร้างไฟล์ app/10_list/page.js — เข้าถึงที่ http://localhost:3000/10_list
import Card from "@/components/Card"
export default function ListPage() {
const blogs = [
{
id: 1,
title: 'เริ่มต้นกับ React',
excerpt: 'เรียนรู้พื้นฐาน React และเริ่มสร้าง UI แบบโต้ตอบได้',
author: 'สมชาย ใจดี',
date: '2025-01-15'
},
{
id: 2,
title: 'คู่มือ Next.js App Router',
excerpt: 'เข้าใจ App Router ใหม่ใน Next.js 13+',
author: 'วีระ รักเรียน',
date: '2025-01-20'
},
{
id: 3,
title: 'เทคนิค CSS',
excerpt: 'เชี่ยวชาญ CSS สมัยใหม่ด้วย Flexbox และ Grid',
author: 'ประยุทธ์ ขยันทำงาน',
date: '2025-01-25'
}
]
const products = [
{ id: 1, name: 'แล็ปท็อป', price: 25000, category: 'อิเล็กทรอนิกส์' },
{ id: 2, name: 'เมาส์', price: 500, category: 'อิเล็กทรอนิกส์' },
{ id: 3, name: 'โต๊ะทำงาน', price: 8000, category: 'เฟอร์นิเจอร์' },
{ id: 4, name: 'เก้าอี้', price: 3000, category: 'เฟอร์นิเจอร์' }
]
return (
<div style={{ padding: '20px' }}>
<h1>List Rendering</h1>
<h2>บทความบล็อก</h2>
{blogs.map((blog) => (
<article key={blog.id} style={{ border: '1px solid #ddd', padding: '15px', margin: '10px 0' }}>
<h3>{blog.title}</h3>
<p>{blog.excerpt}</p>
<small>
โดย {blog.author} | {blog.date}
</small>
</article>
))}
<h2>รายการสินค้า</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '15px' }}>
{products.map((product) => (
<div key={product.id} style={{ border: '1px solid #ddd', padding: '15px', borderRadius: '8px' }}>
<h4>{product.name}</h4>
<p>หมวดหมู่: {product.category}</p>
<p style={{ fontSize: '20px', fontWeight: 'bold', color: 'green' }}>
฿{product.price.toLocaleString('th-TH')}
</p>
</div>
))}
</div>
</div>
)
}javascriptแนวคิด List Rendering#
| Concept | คำอธิบาย |
|---|---|
.map() | แปลงแต่ละ item ใน array |
key prop | จำเป็นสำหรับ React ติดตาม list items อย่างมีประสิทธิภาพ |
key={item.id} | วิธีที่ดีที่สุด: ใช้ ID ที่ไม่ซ้ำกันจากข้อมูล |
key={index} | ทางเลือกสำรอง: ใช้ index (เฉพาะถ้า list คงที่) |
สำคัญ: ต้องระบุ
keyprop ที่ไม่ซ้ำกันเสมอเมื่อแสดงรายการ นี่ช่วยให้ React อัปเดต DOM อย่างมีประสิทธิภาพ
6. useEffect Hook (Live Price Ticker)#
Hook useEffect ใช้บ่อยสำหรับอัปเดตข้อมูลแบบ real-time เช่น ราคาหุ้น, ค่าเงินคริปโต, หรือข้อมูลการซื้อขาย ทำให้คุณรันโค้ดในเวลาที่กำหนดและทำความสะอาดเมื่อเสร็จสิ้น
สร้างไฟล์ app/11_ticker/page.js — เข้าถึงที่ http://localhost:3000/11_ticker
'use client'
import { useState, useEffect } from 'react'
export default function TickerPage() {
const [goldPrice, setGoldPrice] = useState(65000)
const [previousPrice, setPreviousPrice] = useState(null)
const [isRunning, setIsRunning] = useState(true)
// Simulate gold price updates
useEffect(() => {
if (!isRunning) return
const interval = setInterval(() => {
setGoldPrice((currentPrice) => {
setPreviousPrice(currentPrice)
// Generate random price change (-2% to +2%)
const changePercent = (Math.random() - 0.5) * 0.04
const newPrice = Math.max(0, currentPrice * (1 + changePercent))
return Math.round(newPrice)
})
}, 1000) // Update every 1 second
return () => clearInterval(interval)
}, [isRunning])
const priceChange = previousPrice ? goldPrice - previousPrice : 0
const isUp = priceChange >= 0
return (
<div style={{ padding: '20px', backgroundColor: '#f5f5f5', minHeight: '100vh' }}>
<h1>💰 Live Gold Price</h1>
<p style={{ color: '#666' }}>Simulated real-time gold trading price</p>
<div style={{
maxWidth: '500px',
margin: '30px auto',
padding: '30px',
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
}}>
{/* Current Price */}
<div style={{ textAlign: 'center' }}>
<p style={{ fontSize: '14px', color: '#666', marginBottom: '10px' }}>
GOLD PRICE (THB/oz)
</p>
<p style={{
fontSize: '48px',
fontWeight: 'bold',
color: isUp ? '#22c55e' : '#ef4444'
}}>
฿{goldPrice?.toLocaleString()}
</p>
{/* Price Change Indicator */}
{previousPrice && (
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '5px',
marginTop: '10px'
}}>
<span style={{ fontSize: '24px' }}>
{isUp ? '📈' : '📉'}
</span>
<span style={{
fontSize: '18px',
color: isUp ? '#22c55e' : '#ef4444',
fontWeight: 'bold'
}}>
{isUp ? '+' : ''}{priceChange.toLocaleString()}
</span>
<span style={{ fontSize: '14px', color: '#666' }}>
({((priceChange / previousPrice) * 100).toFixed(2)}%)
</span>
</div>
)}
</div>
{/* Control Button */}
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<button
onClick={() => setIsRunning(!isRunning)}
style={{
padding: '10px 20px',
fontSize: '16px',
backgroundColor: isRunning ? '#ef4444' : '#22c55e',
color: 'white',
border: 'none',
borderRadius: '8px',
cursor: 'pointer'
}}
>
{isRunning ? '⏸ Pause' : '▶ Resume'}
</button>
</div>
</div>
</div>
)
}javascriptแนวคิด useEffect#
| Dependency Array | Behavior | Use Case |
|---|---|---|
useEffect(() => {}) | ทำงานหลังทุก render | ใช้น้อย |
useEffect(() => {}, []) | ทำงานครั้งเดียวเมื่อ mount | ดึงข้อมูลครั้งแรก, setup |
useEffect(() => {}, [dep]) | ทำงานเมื่อ dep เปลี่ยน | ตอบสนองการเปลี่ยนแปลงเฉพาะ |
return cleanup | ทำงานก่อน unmount/next effect | เคลียร์ timers, cancel requests |
แนวคิด Live Price Ticker#
| Concept | คำอธิบาย |
|---|---|
setInterval(callback, ms) | รันโค้ดซ้ำทุกๆ X milliseconds |
clearInterval() | หยุด interval (cleanup) |
Math.random() | สร้างตัวเลขสุ่มระหว่าง 0 และ 1 |
Math.max(0, value) | รับรองว่าราคาไม่ติดลบ |
isUp ? '📈' : '📉' | Ternary สำหรับ emoji แบบมีเงื่อนไข |
optional chaining (?.) | เข้าถึง properties อย่างปลอดภัย (goldPrice?.toLocaleString()) |
กรณีการใช้งานจริง#
Pattern นี้ใช้สำหรับ:
- แอปหุ้น/ซื้อขาย — อัปเดตราคาแบบ real-time
- ตลาดคริปโต — ราคา Bitcoin, Ethereum
- เว็บประมูล — อัปเดตการประมูลแบบ real-time
- ผลกีฬา — อัปเดตคะแนนแบบ live
- จำนวนการแจ้งเตือน — รีเฟรชจำนวนที่ยังไม่ได้อ่าน
7. การจัดการอาร์เรย์ (Filter & Sort)#
การทำงานกับ arrays เป็นสิ่งจำเป็นสำหรับแอปที่ขับเคลื่อนด้วยข้อมูล การดำเนินการทั่วไป ได้แก่ การกรอง และการเรียงลำดับ
สร้างไฟล์ app/12_products/page.js — เข้าถึงที่ http://localhost:3000/12_products
'use client'
import { useState } from 'react'
const products = [
{ id: 1, name: 'แล็ปท็อป', category: 'อิเล็กทรอนิกส์', price: 25000 },
{ id: 2, name: 'เมาส์', category: 'อิเล็กทรอนิกส์', price: 500 },
{ id: 3, name: 'โต๊ะทำงาน', category: 'เฟอร์นิเจอร์', price: 8000 },
{ id: 4, name: 'เก้าอี้', category: 'เฟอร์นิเจอร์', price: 3000 },
{ id: 5, name: 'สมุด', category: 'อุปกรณ์เขียน', price: 50 },
{ id: 6, name: 'ปากกา', category: 'อุปกรณ์เขียน', price: 20 },
{ id: 7, name: 'มอนิเตอร์', category: 'อิเล็กทรอนิกส์', price: 5000 },
{ id: 8, name: 'ชั้นวางหนังสือ', category: 'เฟอร์นิเจอร์', price: 4500 },
]
export default function ProductsPage() {
const [category, setCategory] = useState('All')
const [sortBy, setSortBy] = useState('default')
// Filter products by category
const filtered = category === 'All'
? products
: products.filter(p => p.category === category)
// Sort products
const sorted = [...filtered].sort((a, b) => {
if (sortBy === 'price-asc') return a.price - b.price
if (sortBy === 'price-desc') return b.price - a.price
if (sortBy === 'name') return a.name.localeCompare(b.name)
return 0
})
return (
<div style={{ padding: '20px' }}>
<h1>แคตตาล็อกสินค้า</h1>
{/* Filters */}
<div style={{ marginBottom: '20px', display: 'flex', gap: '15px', alignItems: 'center' }}>
<div>
<label>หมวดหมู่: </label>
<select
value={category}
onChange={(e) => setCategory(e.target.value)}
style={{ padding: '8px' }}
>
<option value="All">ทุกหมวดหมู่</option>
<option value="อิเล็กทรอนิกส์">อิเล็กทรอนิกส์</option>
<option value="เฟอร์นิเจอร์">เฟอร์นิเจอร์</option>
<option value="อุปกรณ์เขียน">อุปกรณ์เขียน</option>
</select>
</div>
<div>
<label>เรียงตาม: </label>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
style={{ padding: '8px' }}
>
<option value="default">ค่าเริ่มต้น</option>
<option value="price-asc">ราคา: ต่ำ-สูง</option>
<option value="price-desc">ราคา: สูง-ต่ำ</option>
<option value="name">ชื่อ: ก-ฮ</option>
</select>
</div>
</div>
{/* Results Info */}
<p style={{ color: '#666' }}>
แสดง {sorted.length} สินค้า
</p>
{/* Product Grid */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
gap: '20px'
}}>
{sorted.map((product) => (
<div
key={product.id}
style={{
border: '1px solid #ddd',
borderRadius: '8px',
padding: '15px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}
>
<h3>{product.name}</h3>
<p style={{ color: '#666' }}>{product.category}</p>
<p style={{ fontSize: '24px', fontWeight: 'bold', color: 'green' }}>
฿{product.price.toLocaleString('th-TH')}
</p>
</div>
))}
</div>
</div>
)
}javascriptแนวคิดการจัดการอาร์เรย์#
| Operation | Method | คำอธิบาย |
|---|---|---|
| Filter | .filter(item => condition) | สร้าง array ใหม่ด้วย items ที่ตรงเงื่อนไข |
| Sort | .sort((a, b) => a - b) | เรียงลำดับ array (แก้ไขต้นฉบับ) |
| Spread | [...array] | สร้างสำเนาของ array |
8. API Fetching#
แอปจริงมักต้องดึงข้อมูลจาก external APIs React ใช้ useEffect ร่วมกับ fetch เพื่อรับข้อมูล
ก่อนอื่น สร้าง AttractionCard component พร้อมลิงก์ไปหน้ารายละเอียด:
ไฟล์: components/AttractionCard.js
import Link from 'next/link'
export default function AttractionCard({ attraction }) {
return (
<div
style={{
border: '1px solid #ddd',
borderRadius: '8px',
overflow: 'hidden',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}
>
<Link href={`/13_api/${attraction.id}`}>
<img
src={attraction.coverimage}
alt={attraction.name}
style={{
width: '100%',
height: '200px',
objectFit: 'cover'
}}
/>
</Link>
<div style={{ padding: '15px' }}>
<Link href={`/13_api/${attraction.id}`}>
<h3 style={{ margin: '0 0 10px 0' }}>{attraction.name}</h3>
</Link>
<p style={{
fontSize: '14px',
color: '#666',
margin: '0 0 10px 0',
lineHeight: '1.5'
}}>
{attraction.detail}
</p>
<div style={{ fontSize: '12px', color: '#999' }}>
📍 {attraction.latitude}, {attraction.longitude}
</div>
</div>
</div>
)
}javascriptตอนนี้ใช้ component:
ไฟล์: app/13_api/page.js — เข้าถึงที่ http://localhost:3000/13_api
'use client'
import { useState, useEffect } from 'react'
import AttractionCard from '@/components/AttractionCard'
export default function ApiPage() {
const [attractions, setAttractions] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
// Function to fetch data
const fetchAttractions = async () => {
try {
const response = await fetch('https://www.melivecode.com/api/attractions')
if (!response.ok) {
throw new Error('Failed to fetch data')
}
const data = await response.json()
setAttractions(data)
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
fetchAttractions()
}, []) // Empty dependency array = runs once on component mount
return (
<div style={{ padding: '20px' }}>
<h1>สถานที่ท่องเที่ยว</h1>
<p>ดึงข้อมูลจาก API</p>
{/* Loading State */}
{loading && (
<div style={{ textAlign: 'center', padding: '20px' }}>
<p>กำลังโหลด...</p>
</div>
)}
{/* Error State */}
{error && (
<div style={{ backgroundColor: '#ffebee', padding: '20px', borderRadius: '8px' }}>
<p style={{ color: '#c62828' }}>ข้อผิดพลาด: {error}</p>
</div>
)}
{/* Data Display */}
{!loading && !error && (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: '20px',
marginTop: '20px'
}}>
{attractions.map((attraction) => (
<AttractionCard
key={attraction.id}
attraction={attraction}
/>
))}
</div>
)}
{/* Empty State */}
{!loading && !error && attractions.length === 0 && (
<p>ไม่พบสถานที่ท่องเที่ยว</p>
)}
</div>
)
}javascriptแนวคิด API Fetching#
| Concept | คำอธิบาย |
|---|---|
useState([]) | เริ่มต้น state เป็น empty array สำหรับข้อมูล |
useState(true) | Loading state เริ่มต้นเป็น true |
useState(null) | Error state เริ่มต้นเป็น null |
async/await | วิธีทันสมัยในการจัดการ asynchronous operations |
try/catch/finally | Error handling: try ทำงาน, catch จัดการ errors, finally ทำงานเสมอ |
fetch(url) | ส่งคำขอ HTTP ไปยัง API |
response.json() | แปลง response เป็น JSON |
response.ok | ตรวจสอบว่า request สำเร็จ (status 200-299) |
States ของ API Fetching#
| State | Purpose | UI Shows |
|---|---|---|
loading: true | ระหว่างรอ response | ตัวบ่งชี้ loading/spinner |
error: message | เมื่อ request ล้มเหลว | ข้อความ error |
data: [] | เมื่อ request สำเร็จ | ข้อมูล/เนื้อหาจริง |
data.length === 0 | สำเร็จแต่ไม่มีผลลัพธ์ | ข้อความ empty state |
9. URL Parameters & Dynamic Routes#
Next.js App Router ใช้โครงสร้างโฟลเดอร์พร้อมวงเล็บเหลี่ยม [param] เพื่อสร้าง dynamic routes
หมายเหตุ: อัปเดต AttractionCard component (จาก section 8) ให้มีลิงก์ไปหน้ารายละเอียดโดยใช้ id parameter:
import Link from 'next/link'
export default function AttractionCard({ attraction }) {
return (
<div>
<Link href={`/13_api/${attraction.id}`}>
<img src={attraction.coverimage} alt={attraction.name} />
</Link>
<div>
<Link href={`/13_api/${attraction.id}`}>
<h3>{attraction.name}</h3>
</Link>
{/* ... เนื้อหาอื่นๆ ของ card */}
</div>
</div>
)
}javascriptสร้างโครงสร้างโฟลเดอร์ app/13_api/[id]/ และไฟล์ page.js:
ไฟล์: app/13_api/[id]/page.js — เข้าถึงที่ http://localhost:3000/13_api/1, /13_api/2, ฯลฯ
// ไฟล์นี้ใช้ Server Component โดย default (ไม่ต้องมี 'use client')
// params prop ถูกส่งมาให้อัตโนมัติโดย Next.js
export default async function AttractionDetailPage({ params }) {
const { id } = await params
// Fetch data for specific attraction
const response = await fetch(`https://www.melivecode.com/api/attractions/${id}`)
const data = await response.json()
// Handle error case
if (response.status === 404 || !data.attraction.id) {
return (
<div style={{ padding: '20px' }}>
<h1>ไม่พบสถานที่ท่องเที่ยว</h1>
<p>สถานที่ท่องเที่ยว ID "{id}" ไม่มีอยู่</p>
<a href="/13_api">← กลับไปหน้าทั้งหมด</a>
</div>
)
}
return (
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
<a href="/13_api" style={{ textDecoration: 'none', color: '#0070f3' }}>
← กลับไปหน้าทั้งหมด
</a>
<img
src={data.attraction.coverimage}
alt={data.attraction.name}
style={{
width: '100%',
height: '400px',
objectFit: 'cover',
borderRadius: '8px',
marginTop: '20px'
}}
/>
<h1 style={{ fontSize: '36px', margin: '20px 0 10px' }}>{data.attraction.name}</h1>
<p style={{
fontSize: '18px',
lineHeight: '1.8',
color: '#333'
}}>
{data.attraction.detail}
</p>
<div style={{
marginTop: '30px',
padding: '20px',
backgroundColor: '#f5f5f5',
borderRadius: '8px'
}}>
<h3>ตำแหน่ง</h3>
<p>ละติจูด: {data.attraction.latitude}</p>
<p>ลองจิจูด: {data.attraction.longitude}</p>
<a
href={`https://www.google.com/maps?q=${data.attraction.latitude},${data.attraction.longitude}`}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#0070f3' }}
>
ดูบน Google Maps →
</a>
</div>
<p style={{ marginTop: '20px', fontSize: '14px', color: '#999' }}>
สร้างเมื่อ: {new Date(data.attraction.createdAt).toLocaleDateString('th-TH')}
</p>
</div>
)
}javascriptแนวคิด Dynamic Route#
| Concept | คำอธิบาย |
|---|---|
[id] folder | สร้าง dynamic route segment |
params prop | มี route parameters (ส่งอัตโนมัติโดย Next.js) |
await params | ต้องใช้ใน Next.js 15+ เพื่อเข้าถึง params |
data.attraction | API response ห่อข้อมูลใน attraction object |
| Server Component | Default, ไม่ต้องมี 'use client' |
async function | สามารถใช้ async/await ใน Server Components |
10. Client vs Server Components (Next.js)#
Next.js 13+ แนะนำสองประเภทของ components:
Server Components (Default)#
- ไม่ต้องมี directive - Components เป็น Server Components โดย default
- Render บนเซิร์ฟเวอร์ - HTML ถูกสร้างขึ้นบน server-side
- เข้าถึง databases และ APIs โดยตรง - ปลอดภัยจากการเปิดเผย secrets
- ขนาด bundle เล็กกว่า - ส่ง JavaScript ไปเบราว์เซอร์น้อยกว่า
- SEO ดีกว่า - Content ถูก render ใน HTML
- ใช้ hooks ไม่ได้ - ไม่มี
useState,useEffect, หรือ event handlers - ใช้สำหรับ: ดึงข้อมูล, database queries, static content, API routes
Client Components#
- ต้องมี directive
'use client'- ต้องอยู่บนสุดของไฟล์ - Render ในเบราว์เซอร์ - Interactivity เกิดขึ้นบน client-side
- ใช้ React hooks ได้ -
useState,useEffect,useContext, ฯลฯ - Event handlers -
onClick,onChange,onSubmit, ฯลฯ - Browser APIs -
localStorage,window,navigator, ฯลฯ - ขนาด bundle ใหญ่ขึ้น - ส่ง JavaScript ไปเบราว์เซอร์มากขึ้น
- ใช้สำหรับ: Interactive UI, forms, real-time updates, browser APIs
เมื่อไหรควรใช้แบบไหน#
| ใช้ Server Components สำหรับ | ใช้ Client Components สำหรับ |
|---|---|
| ดึงข้อมูลจาก APIs | useState, useEffect hooks |
| Database queries | Event handlers (onClick, onChange) |
| Static content | Real-time updates |
| หน้าที่สำคัญสำหรับ SEO | Browser APIs (localStorage) |
| การทำงานที่ละเอียดอ่อน (API keys) | Interactive forms |
Tip: ใช้ Server Components เป็น default และเพิ่ม
'use client'เมื่อต้องการ interactivity หรือ features ที่ทำงานเฉพาะในเบราว์เซอร์เท่านั้น
อัปเดตสารบัญ#
อัปเดต app/page.js เพื่อรวมหน้าทั้งหมด:
import Link from 'next/link'
export default function Home() {
return (
<div style={{ padding: '20px' }}>
<h1>บทเรียน JavaScript & React</h1>
<h2>ส่วนที่ 3: พื้นฐาน JavaScript</h2>
<ul>
<li><Link href="/01_var">ตัวแปร</Link></li>
<li><Link href="/02_array">อาร์เรย์</Link></li>
<li><Link href="/03_object">ออบเจกต์</Link></li>
<li><Link href="/04_array_objects">อาร์เรย์ของออบเจกต์</Link></li>
<li><Link href="/05_function">ฟังก์ชัน</Link></li>
</ul>
<h2>ส่วนที่ 4: พื้นฐาน React</h2>
<ul>
<li><Link href="/06_show_hide">Show/Hide & Toggle (useState)</Link></li>
<li><Link href="/07_forms">แบบฟอร์ม & Controlled Components</Link></li>
<li><Link href="/08_conditional">Conditional Rendering</Link></li>
<li><Link href="/09_components">Components ที่ใช้ซ้ำได้</Link></li>
<li><Link href="/10_list">List Rendering</Link></li>
<li><Link href="/11_ticker">useEffect Hook (Live Ticker)</Link></li>
<li><Link href="/12_products">Filter & Sort</Link></li>
<li><Link href="/13_api">API Fetching</Link></li>
<li><Link href="/13_api/1">Dynamic Routes [id]</Link></li>
<li><Link href="/15_client_server">Client vs Server Components</Link></li>
</ul>
</div>
)
}javascriptสรุป#
ส่วนที่ 1: HTML#
- ใช้สำหรับสร้างโครงสร้างและเนื้อหาหน้าเว็บ
- Elements สำคัญ: หัวข้อ ย่อหน้า ลิงก์ รูปภาพ รายการ แบบฟอร์ม
- ใช้ semantic HTML เพื่อ accessibility ที่ดีขึ้น
ส่วนที่ 2: CSS#
- ใช้สำหรับตกแต่งหน้าเว็บ
- 3 วิธีในการใช้ CSS: inline, internal, external
- ใช้ไฟล์ CSS ภายนอกเพื่อการจัดการที่ดีขึ้น
- Flexbox สำหรับ responsive layouts
ส่วนที่ 3: JavaScript กับ Next.js#
- React/Next.js สำหรับเว็บแอปพลิเคชันแบบไดนามิก
- ตัวแปร:
const(ค่าไม่เปลี่ยน),let(ค่าเปลี่ยนได้) - อาร์เรย์สำหรับรายการข้อมูล
- ออบเจกต์สำหรับข้อมูลแบบ key-value
- อาร์เรย์ของออบเจกต์สำหรับโครงสร้างข้อมูลที่ซับซ้อน
- ฟังก์ชันสำหรับนำโค้ดกลับมาใช้ใหม่ (ฟังก์ชันปกติและ arrow function)
- ใช้
map()เพื่อแสดงรายการใน React
ส่วนที่ 4: พื้นฐาน React#
useStatehook สำหรับจัดการ state ของ component (toggle buttons, show/hide)- Event handling:
onClick,onChange,onSubmit,onKeyPress - Controlled components ด้วย
valueและonChange - Conditional rendering: ternary,
&&operator - สร้าง components ที่ใช้ซ้ำได้ด้วย props
- Component composition และ import/export
- List rendering ด้วย
.map()และ keys useEffecthook สำหรับ side effects และ timers- การจัดการอาร์เรย์: filtering, sorting, pagination
- ดึงข้อมูลจาก APIs ด้วย
fetchและuseEffect - จัดการ loading, error, และ success states
- Dynamic routes ด้วย URL parameters
[id] - Client Components vs Server Components
ขั้นตอนต่อไป#
- ฝึกสร้างหน้า HTML กับ elements ต่างๆ
- ลองใช้ CSS styling และ Flexbox layouts
- สร้างโปรเจกต์ React/Next.js เล็กๆ ด้วยสิ่งที่เรียนมา
- ศึกษา JavaScript ขั้นสูงเพิ่มเติม (conditionals, loops, async/await)
- เรียนรู้ React hooks เพิ่มเติม (
useContext,useReducer,useCallback,useMemo) - ศึกษาการตรวจสอบแบบฟอร์มและการจัดการ errors
- สำรวจรูปแบบการยืนยันตัวตนและการอนุญาต
- เรียนรู้การผนวก database ด้วย Prisma หรือ ORMs อื่นๆ
- สร้างแอป full-stack ด้วย Next.js API routes