Back

HTML, CSS, JavaScript, React & Next.js 101Blur image

บทความนี้จะแนะนำพื้นฐานการพัฒนาเว็บ โดยเริ่มจาก 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 เริ่มต้น:

html:5

<!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

Heading and paragraph

ลิงก์ (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

Link and image

รายการ (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

Form

องค์ประกอบของแบบฟอร์ม#

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>
html

CSS คืออะไร?#

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>
html

External 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 เพื่อให้เบราว์เซอร์โหลดอัตโนมัติเมื่อมีการเปลี่ยนแปลงโค้ด:

  1. ไปที่ Extensions
  2. ค้นหา “Five Server”
  3. ติดตั้งส่วนเสริม
  4. คลิกขวาที่ 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 เข้าด้วยกัน
.classnameCSS class selector (เริ่มต้นด้วยจุด)
#idnameCSS 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#

PropertyValueคำอธิบาย
list-style-typenoneลบ bullet points ออกจากรายการ
margin0ลบระยะห่างภายนอก
padding0ลบระยะห่างภายใน
background-colordarkgreenกำหนดสีพื้นหลัง
overflowhiddenครอบ elements ที่ลอยอยู่ภายใน parent
floatleftวางรายการแนวนอน
text-decorationnoneลบเส้นใต้ลิงก์
displayblockทำให้ทั้งพื้นที่คลิกได้

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

ปุ่มลัดที่มีประโยชน์#

ActionWindows/LinuxmacOS
คัดลอกCtrl + CCmd + C
วางCtrl + VCmd + V
บันทึกCtrl + SCmd + S
ตัดCtrl + XCmd + X
เลิกทำCtrl + ZCmd + Z
ทำซ้ำCtrl + YCmd + 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 dev
bash

เปิดเบราว์เซอร์และไปที่ 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) กลับ
classNameReact ใช้ className แทน class
JSXJavaScript + 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

รูปแบบฟังก์ชัน#

TypeSyntaxUse Case
Function Declarationfunction name() {}ใช้ทั่วไป เรียกได้ก่อนประกาศ
Function Expressionconst name = function() {}เมื่อกำหนดให้ตัวแปร
Arrow Functionconst name = () => {}สั้น ทันสมัย ใช้บ่อยใน React
Arrow (Implicit Return)const name = () => valueฟังก์ชันบรรทัดเดียว ไม่ต้องมี return

การตั้งชื่อฟังก์ชัน#

ConventionRuleValid ExamplesInvalid
camelCaseคำแรกพิมพ์เล็ก คำต่อไปพิมพ์ใหญ่getUserData, calculateTotal, isValidGetUserData, get_user_data
Verb-firstเริ่มด้วยคำกริยาที่อธิบายการกระทำfetchUser, saveData, deleteItemuser, data, item
Booleansใช้คำนำหน้า is, has, can, shouldisLoggedIn, hasPermission, canEditlogged, 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: ค่าปัจจุบันและฟังก์ชันอัปเดต
!isVisibleNOT 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
htmlForReact เทียบเท่า 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#

MethodSyntaxUse 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 คงที่)

สำคัญ: ต้องระบุ key prop ที่ไม่ซ้ำกันเสมอเมื่อแสดงรายการ นี่ช่วยให้ 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 ArrayBehaviorUse 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

แนวคิดการจัดการอาร์เรย์#

OperationMethodคำอธิบาย
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/finallyError handling: try ทำงาน, catch จัดการ errors, finally ทำงานเสมอ
fetch(url)ส่งคำขอ HTTP ไปยัง API
response.json()แปลง response เป็น JSON
response.okตรวจสอบว่า request สำเร็จ (status 200-299)

States ของ API Fetching#

StatePurposeUI 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.attractionAPI response ห่อข้อมูลใน attraction object
Server ComponentDefault, ไม่ต้องมี '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 สำหรับ
ดึงข้อมูลจาก APIsuseState, useEffect hooks
Database queriesEvent handlers (onClick, onChange)
Static contentReal-time updates
หน้าที่สำคัญสำหรับ SEOBrowser 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#

  • useState hook สำหรับจัดการ 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
  • useEffect hook สำหรับ side effects และ timers
  • การจัดการอาร์เรย์: filtering, sorting, pagination
  • ดึงข้อมูลจาก APIs ด้วย fetch และ useEffect
  • จัดการ loading, error, และ success states
  • Dynamic routes ด้วย URL parameters [id]
  • Client Components vs Server Components

ขั้นตอนต่อไป#

  1. ฝึกสร้างหน้า HTML กับ elements ต่างๆ
  2. ลองใช้ CSS styling และ Flexbox layouts
  3. สร้างโปรเจกต์ React/Next.js เล็กๆ ด้วยสิ่งที่เรียนมา
  4. ศึกษา JavaScript ขั้นสูงเพิ่มเติม (conditionals, loops, async/await)
  5. เรียนรู้ React hooks เพิ่มเติม (useContext, useReducer, useCallback, useMemo)
  6. ศึกษาการตรวจสอบแบบฟอร์มและการจัดการ errors
  7. สำรวจรูปแบบการยืนยันตัวตนและการอนุญาต
  8. เรียนรู้การผนวก database ด้วย Prisma หรือ ORMs อื่นๆ
  9. สร้างแอป full-stack ด้วย Next.js API routes

แหล่งข้อมูลเพิ่มเติม#

HTML, CSS, JavaScript, React & Next.js 101
ผู้เขียน กานต์ ยงศิริวิทย์ / Karn Yongsiriwit
เผยแพร่เมื่อ March 21, 2026
ลิขสิทธิ์ CC BY-NC-SA 4.0

กำลังโหลดความคิดเห็น...

ความคิดเห็น 0