หน้าหลัก/บทเรียนขั้นสูง/10. การรับข้อมูลจากผู้ใช้ (Forms & Inputs)
เวลาเรียนโดยประมาณ: 45 นาที (ฉบับปูพื้นฐานละเอียดพิเศษ)
Chapter 10: Beginner Friendly - Forms & Inputs

การรับข้อมูลจากผู้ใช้ in React ควบคุมกระดานข้อมูลและสร้างใบสมัครอัจฉริยะ!

ยินดีต้อนรับสู่บทเรียนที่ 10! บทนี้ครูจะพามาเรียนรู้วิธีการทำระบบเชื่อมโยงระหว่าง "คีย์บอร์ดที่ผู้ใช้พิมพ์"กับ "สมองความจำของ React (State)" เพื่อนำไปสร้างใบสมัคร ปุ่มกรองข้อมูล และระบบตรวจสอบความถูกต้องแบบเรียลไทม์!

React Forms Holographic Glowing Input Fields Illustration

พจนานุกรมปูพื้นฐานแบบฟอร์ม (Beginner's Form Dictionary)

หากคุณไม่เคยมีพื้นฐานด้านการเขียนแบบฟอร์มบนเว็บไซต์มาก่อน ขอให้อ่านคำศัพท์และตัวอย่างอุปมาอุปไมยต่อไปนี้ก่อนเลย:

1. Input Field (ช่องกรอกข้อมูล)

เปรียบเสมือน "กล่องส่งเอกสารว่างเปล่า" ที่เบราว์เซอร์เปิดพื้นที่ไว้ให้ผู้ใช้พิมพ์ข้อความ ตัวเลข หรือคลิกติ๊กเลือก เช่น ช่องใส่ Username หรือช่องใส่รหัสผ่าน

2. Form Wrapper (กรอบฟอร์ม `<form>`)

เปรียบเสมือน "ซองจดหมายใบใหญ่" ที่ใช้รวบรวมช่องกรอกทั้งหมดมามัดรวมกัน เพื่อกดส่งเอกสารสมัครสมาชิกหรือส่งข้อความไปหาเซิร์ฟเวอร์พร้อมกันในคลิกเดียว

3. onChange Event (หูดักฟังเสียงคีย์บอร์ด)

เปรียบเสมือน "เซนเซอร์จับการขยับนิ้ว" ที่คอยส่องช่องกรอกตลอดเวลาว่าผู้ใช้กดลบหรือเพิ่มตัวอักษรไหม ทันทีที่มีการพิมพ์ เซนเซอร์นี้จะทำงานเพื่อรายงานค่าใหม่ส่งไปให้ React รู้ทันที

4. e.target.value (สารสกัดตัวหนังสือ)

คือคำสั่งพิเศษในการแกะตัวเอกสารเพื่อบอกว่า "ณ วินาทีนี้ผู้ใช้งานพิมพ์ตัวอักษรอะไรค้างไว้ในกล่องบ้าง" ช่วยดึงคำดิบนั้นส่งไปบันทึกเก็บในสเตตัสหน่วยความจำ

1กระบวนทัศน์ใหม่: ทำไมแบบฟอร์มใน React ถึงทำงานไม่เหมือนเว็บดั้งเดิม?

ในเว็บไซต์ยุคเก่า เมื่อผู้ใช้พิมพ์ข้อความลงในช่องกรอก ข้อมูลคำเหล่านั้นจะถูกเก็บอยู่ภายใน "ตัวของกล่องอินพุตนั้นเอง (DOM)"โดยที่ระบบหลังบ้านไม่มีทางรู้ได้เลยว่าผู้ใช้พิมพ์อะไรอยู่ จนกว่าผู้ใช้จะกดปุ่ม "ส่งข้อมูล (Submit)" เพื่อยื่นเอกสารทั้งหมดทีเดียว

แต่ในโลกของ React เรามีแนวคิดที่ทรงพลังกว่านั้นมาก เรียกว่า Controlled Component (คอมโพเนนต์แบบควบคุม)

เรื่องเล่าเปรียบเทียบ: แก้วน้ำวิเศษ กับ แก้วน้ำธรรมดา

ลองนึกภาพช่องกรอกปกติเป็น "แก้วน้ำธรรมดา" (Uncontrolled) น้ำที่ผู้ใช้เทใส่คือปริมาณตัวหนังสือ แก้วเก็บน้ำไว้ในตัวเอง ถ้าเราอยากรู้ว่าเหลือน้ำเท่าไหร่ เราต้องยกไม้บรรทัดมาจุ่มวัดปริมาตรเองทุกครั้ง (เปรียบเหมือนการใช้ Javascript ไปสอยค่าจากหน้าจอ)

ในทางกลับกัน "แก้วน้ำวิเศษของ React" (Controlled) จะไม่มีพื้นที่จุน้ำเป็นของตัวเองเลย แต่มันจะต่อท่อเชื่อมกับ "ถังเก็บน้ำอัจฉริยะ (State)" เสมอ! เมื่อมีน้ำไหลเข้ามาในแก้วท่อจะส่งน้ำไปเข้าถังความจำหลักทันที และถังหลักจะใช้อาคมสั่งให้ระดับน้ำในแก้ววิเศษลอยตัวขึ้นสูงให้ตรงกับปริมาณในถังอัจฉริยะตลอดเวลา

ผลลัพธ์คือ **ถังความจำหลัก (State) จะกลายเป็นแหล่งข้อมูลที่ถูกต้องที่สุดเพียงแหล่งเดียว (Single Source of Truth)** ทำให้เราสามารถตรวจจับได้ทันทีว่า ณ วินาทีนี้ มีหยดน้ำไหลเข้ามาเท่าไหร่ เพื่อปรับเปลี่ยนอนิเมชันหรือแจ้งเตือนผู้ใช้งานได้แบบสดๆ ทันที!

2วิธีสร้างช่องกรอกข้อมูลเดี่ยว (Single Input) ทีละขั้นตอนอย่างละเอียด

ลองมาดูโครงสร้างพิมพ์เขียวของการทำ Controlled Input สำหรับกรอกชื่อรหัสนักบินกัน:

ช่องกรอกข้อมูลผูก State แบบเดี่ยว
import { useState } from "react"; function PilotInput() { // 1. ตั้งถังความจำสำรองสำหรับรับชื่อ (เริ่มต้นเป็นสตริงว่างเปล่า "") const [name, setName] = useState(""); // 2. ฟังก์ชันดักตรวจนิ้วสัมผัสแป้นคีย์บอร์ด const handleType = (event) => { // ดึงตัวหนังสือล่าสุดที่พึ่งกรอกลงในช่องออกมา แล้วหยอดใส่ถังความจำ setName(event.target.value); }; return ( <div className="form-group"> <label>กรอกรหัสนักบิน:</label> {/* 3. การผูกข้อมูล (Controlled Binding): - value={name} บังคับให้อินพุตแสดงตัวหนังสือตามที่เก็บไว้ใน State เท่านั้น - onChange={handleType} ทุกครั้งที่ผู้ใช้ขยับแป้นพิมพ์ ให้วิ่งไปรันฟังก์ชันด้านบนเพื่ออัปเดต State */} <input type="text" value={name} onChange={handleType} placeholder="พิมพ์รหัส..." /> {/* 4. โชว์ผลลัพธ์เรียลไทม์ข้างใต้ช่องกรอกได้ทันที! */} <p>ยินดีต้อนรับนักบิน: {name}</p> </div> ); }

แกะโค้ดอย่างละเอียด:

  • เมื่อเราพิมพ์ตัวอักษร "A" ลงไป → เบราว์เซอร์จะปล่อยสัญญาณเหตุการณ์การเปลี่ยนแปลงขึ้นมา
  • ฟังก์ชัน onChange จะจับสัญญาณแล้ววิ่งไปเรียก handleType(event)
  • ในฟังก์ชัน event.target.value จะได้ข้อความสะสมทั้งหมดคือ "A"
  • คำสั่ง setName("A") จะถูกประมวลผล สั่งให้ React วาด Component นี้ใหม่อีกรอบ (Re-render)
  • ในรอบการวาดใหม่นี้ ค่าตัวแปร name จะมีค่าเป็น "A" ส่งผลให้ช่องกรอกอัปเดตอักษรและตัวหนังสือข้างใต้สว่างคำว่า "ยินดีต้อนรับนักบิน: A" ทันที!

3การรับมือช่องกรอกจำนวนมาก (Multiple Inputs) สไตล์วิศวกรมืออาชีพ

หากหน้าแอปของเราต้องการเก็บข้อมูลสมัครสมาชิก ซึ่งมีช่องให้พิมพ์ทั้ง ชื่อเล่น, อีเมล, รหัสผ่าน, และประเภทของยานหากเราใช้วิธีประกาศ useState แยกทีละตัว โค้ดจะยาวเหยียดและอ่านยากมาก

// 🚨 วิธีแบบนี้ใช้งานได้จริงแต่ทำให้โค้ดบวม ล้น และดูแลรักษายากมากเมื่อช่องพิมพ์เพิ่มขึ้น
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [shipType, setShipType] = useState("standard");

วิธีแก้ปัญหาที่ดีที่สุด: มัดรวมอินพุตทั้งหมดให้กลายเป็น "Object ตัวเดียว" และใช้เทคนิคเปลี่ยนคีย์แบบไดนามิก!

แรปเปอร์ฟังก์ชันคุมฟอร์มแบบอเนกประสงค์
import { useState } from "react"; function RegistrationForm() { // 1. ยุบถังความจำรวมไว้ในออบเจกต์ชิ้นเดียว const [formData, setFormData] = useState({ username: "", email: "", shipClass: "explorer" }); // 2. ฟังก์ชันครอบจักรวาล ดูแลอินพุตทุกตัวพร้อมกัน! const handleChange = (event) => { const { name, value } = event.target; // อัปเดตเฉพาะคีย์ที่พิมพ์โดยใช้ไวยากรณ์สเปรดโอเปอเรเตอร์ (...prev) เพื่อคัดลอกค่าเดิมไว้ setFormData(prev => ({ ...prev, [name]: value // 💡 เติมวงเล็บเหลี่ยมช่วยเลือกอัปเดตตามชื่อช่องกรอกทันที! })); }; return ( <form> {/* ⚠️ คีย์สำคัญ: ตั้งค่า attribute 'name' ให้ตรงกับคีย์ที่เขียนใน State ด้านบน */} <input type="text" name="username" // 👈 ชื่อตรงกับ formData.username value={formData.username} onChange={handleChange} /> <input type="email" name="email" // 👈 ชื่อตรงกับ formData.email value={formData.email} onChange={handleChange} /> </form> ); }

4ระบบยื่นส่งฟอร์ม (onSubmit) และการลั่นสมอหยุดจอโหลดด้วย e.preventDefault()

พฤติกรรมดั้งเดิมของเบราว์เซอร์เมื่อกดยื่นใบสมัคร (Submit Form) คือ มันจะสั่ง **โหลดหน้าจอและรีเฟรชเว็บใหม่ 1 ครั้งทันที** เพื่อพยายามส่งพารามิเตอร์ขึ้นไปบน URL ของเบราว์เซอร์

แต่ในเว็บแอปพลิเคชันยุคใหม่ (SPA - Single Page Application) เช่น Next.js หรือ React เราต้องการจัดการข้อมูลทุกอย่างแบบไร้รอยต่อ โดยไม่จำเป็นต้องมีการกระพริบตาหรือรีโหลดของหน้าจอเลย เพื่อส่งข้อมูลไปหาเซิร์ฟเวอร์หลังบ้านผ่าน API

เพื่อแก้ไขเรื่องนี้ เราจึงมีคำสั่งพิเศษที่ชื่อว่า e.preventDefault() ยื่นป้ายสั่งเบรกเบราว์เซอร์ไว้:

การสอยความกระพริบออกโดยอัตโนมัติ
const handleSubmit = (event) => { // 🛡️ ดึงเบรกฉุกเฉินเบราว์เซอร์ ห้ามรีเฟรชหน้าเว็บเด็ดขาด! event.preventDefault(); // ณ จุดนี้ หน้าเว็บจะไม่มีการกระพริบโหลดใหม่ใดๆ ทั้งสิ้น // เราสามารถดึงข้อมูลใน State ส่งไปบันทึกผ่านระบบ API ได้เลยอย่างลื่นไหล console.log("ส่งข้อมูลเข้าระบบเรียบร้อย:", formData); }; return ( <form onSubmit={handleSubmit}> <button type="submit">ยืนยันสมัครอวกาศ</button> </form> );

5ระบบตรวจข้อมูลย้อนกลับ (Form Validation) แบบสดเรียลไทม์

ประโยชน์สูงสุดของแนวคิด Controlled Component คือเราสามารถสร้างข้อความเตือนภัยเวลาผู้ใช้กรอกข้อมูลไม่ถูกต้องได้แบบวินาทีต่อวินาที (Instant Validation) เช่น ถ้ารหัสผ่านยาวน้อยกว่า 6 ตัวอักษร ให้ปุ่มส่งฟอร์มถูกล็อกและแสดงตัวอักษรสีแดงเตือนทันที!

ลองมาดูการเขียนสลักล็อกปุ่มส่งฟอร์มง่ายๆ ดังนี้:

ตัวอย่างการสร้างเงื่อนไขจำกัดระบบกรอก
function CadetJoin() { const [name, setName] = useState(""); const [agree, setAgree] = useState(false); // 1. ตั้งเงื่อนไขตรวจเช็คความยาวและสัญญากติกา const isNameInvalid = name.trim().length < 4; const isFormLocked = isNameInvalid || !agree; return ( <div> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> {/* 2. แสดงตัวอักษรสีแดงเตือนภัยเรียลไทม์เฉพาะเมื่อผู้ใช้เริ่มพิมพ์แล้วแต่ยังสั้นอยู่ */} {name.length > 0 && isNameInvalid && ( <p className="text-red">🚨 ชื่อนักบินจำเป็นต้องมีความยาว 4 อักษรขึ้นไป</p> )} {/* 3. ล็อคปุ่มส่งใบสมัครอย่างสมบูรณ์แบบไดนามิก */} <button disabled={isFormLocked}> ยื่นใบสมัครเข้าสู่ดาวสำรวจ </button> </div> ); }

แท่นทดลองการผูกแบบฟอร์มอวกาศสด (Space Cadet Sandbox Deck)

มิกะสามารถสลับแท็บเพื่อดูความต่างระหว่าง **Controlled (ผูกข้อมูลสด)**, **Live Validation (ตรวจกรอกผิด)**, และ **Uncontrolled (ปล่อยตามมีตามเกิด)** โดยสามารถพิมพ์ในช่องและสังเกตความจำในถังเก็บ (State Inspector) ได้ทันที

แบบฟอร์มรับใบสมัครนักบินอวกาศ (Controlled Form)

* ค่ายืดหยุ่นตาม State: formData.cadetName
ค่าสถานะสมองจำรองในหน่วยความจำ (React State Inspector)
// คอยแอบส่องสเตตัสในหน่วยความจำในวินาทีนี้
const [formData, setFormData] = useState({
  cadetName: "",<br/>  spaceClass: "explorer",<br/>  agreeToTerms: false<br/>});
แบล็กออนไลฟ์การ์ด (REALTIME BADGE)
Zoon Space Academy
ชื่ออ้างอิงรหัสนักบิน (CADET NAME CODE)

รอพิมพ์ชื่อที่ฟอร์ม...

SHIP CLASS:EXPLORER
STATUS:LOCKED (สัญญายังไม่เซ็น)
SECTOR: 7-A (ORBIT)SYSTEM ACTIVE
Controlled Component: React state เป็นตัวกำหนด 'ข้อมูลในอินพุต' และเปลี่ยนเมื่อดักจับ onChange

ประลองสมองบทที่ 9 (Quiz & Exercises)

ครูได้เตรียมข้อสอบชิงรางวัลจำนวน 5 ข้อสำหรับการฝึกวัดระดับความเข้าใจเรื่องการเขียนแบบฟอร์มและการรับค่าอินพุต ลองทำดูทีละขั้นเพื่อทบทวนความรู้กันนะ!

🧠 ทบทวนความเข้าใจบทที่ 10 (Forms & Inputs Quiz)

ข้อ 1 จาก 5

Q:Controlled Component ใน React หมายความว่าอย่างไร?

💡

ลองมาดูแนวคำอธิบายเพิ่มเติมกัน...

ถูกต้องที่สุด! Controlled Component คืออินพุตที่ถูกสั่งล็อกผูกค่าของมันไว้ใน State ของ React (ผ่าน value={state}) เมื่อพิมพ์ข้อมูลเข้ามา ฟังก์ชัน onChange จะทำหน้าที่ส่งตัวหนังสือใหม่ไปอัปเดต State นั้น ทำให้ React เป็นผู้คุมข้อมูลเบ็ดเสร็จ เรียกว่า 'Single Source of Truth'

บทเรียนที่ 10 สำเร็จการศึกษาเรียบร้อย!

บทถัดไป: การจัดการเวลา (Async / Await / Promises) (บทเรียนที่ 11)

ยินดีด้วย! คุณสามารถรับและส่งฟอร์มข้อมูลได้อย่างชำนาญแล้ว บทเรียนต่อไปเราจะไปลุยเรื่องหัวกะทิ: การดึงข้อมูลล่าช้าจาก API และการรับมือกับการตอบสนองที่ไม่พร้อมกัน

บทเรียนถัดไป