- การทำ Form Validation ด้วย React State (useState)
- 1. แนวคิดพื้นฐาน
- 2. สร้าง Form State
- 3. จัดการ Input ด้วย onChange
- 4. สร้าง Validation Function
- 5. ตรวจสอบรูปแบบ Email
- 6. ตรวจสอบข้อมูลตอน Submit
- 7. ตัวอย่าง Complete Form
- 8. Validation แบบ Real-time
- 9. Validation ตอน onBlur
- 10. เพิ่ม Touched State
- 11. ตัวอย่าง Validation หลายเงื่อนไข
- 12. Checkbox Validation
- 13. Select Validation
- 14. Disable Submit Button
- 15. Validation ฝั่ง Client ไม่เพียงพอ
- 16. React State Validation เหมาะกับงานแบบไหน
- 17. เปรียบเทียบแนวทาง
- 18. Best Practices
- สรุป
#การทำ Form Validation ด้วย React State (useState)
การตรวจสอบข้อมูลในฟอร์ม หรือ Form Validation เป็นส่วนสำคัญของ Web Application เช่น การตรวจสอบว่า Email ถูกต้องหรือไม่, Password มีความยาวเพียงพอหรือไม่ หรือผู้ใช้กรอกข้อมูลที่จำเป็นครบหรือยัง
ใน React เราสามารถทำ Validation ได้โดยไม่ต้องใช้ Library เพิ่มเติม เช่น Formik, React Hook Form, Yup หรือ Zod โดยใช้ useState เพื่อจัดการทั้งค่าของฟอร์มและข้อความ Error
บทความนี้จะแสดงแนวทางการทำ Validation ด้วย React State ตั้งแต่พื้นฐานจนถึงการนำไปใช้งานจริง
#1. แนวคิดพื้นฐาน
ฟอร์มแบบ Controlled Component ใน React จะเก็บค่าของ Input ไว้ใน State
ตัวอย่าง:
import { useState } from "react";
function App() {
const [email, setEmail] = useState("");
return (
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);
}
export default App;
เมื่อผู้ใช้พิมพ์ข้อมูล ค่าใน <input> จะถูกเก็บไว้ใน State
แนวคิดหลักคือ
User Input
↓
onChange
↓
React State
↓
Validation
↓
Error Message
#2. สร้าง Form State
หากฟอร์มมีหลาย Field ควรเก็บข้อมูลไว้ใน Object เดียว
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
});
และสร้าง State สำหรับ Error
const [errors, setErrors] = useState({});
โครงสร้างของ Error อาจเป็น
{
name: "กรุณากรอกชื่อ",
email: "รูปแบบ Email ไม่ถูกต้อง",
password: "Password ต้องมีอย่างน้อย 8 ตัวอักษร"
}
#3. จัดการ Input ด้วย onChange
สามารถสร้าง Function เดียวสำหรับจัดการ Input ทุก Field
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};
ตัวอย่าง Input
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
ข้อดีคือไม่จำเป็นต้องสร้าง handleNameChange, handleEmailChange หรือ handlePasswordChange แยกกัน
#4. สร้าง Validation Function
สามารถแยก Validation Logic ออกมาเป็น Function
const validate = () => {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = "กรุณากรอกชื่อ";
}
if (!formData.email.trim()) {
newErrors.email = "กรุณากรอก Email";
}
if (!formData.password) {
newErrors.password = "กรุณากรอก Password";
} else if (formData.password.length < 8) {
newErrors.password = "Password ต้องมีอย่างน้อย 8 ตัวอักษร";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
หากไม่มี Error
Object.keys(newErrors).length === 0
จะได้ค่า true
#5. ตรวจสอบรูปแบบ Email
สามารถใช้ Regular Expression ตรวจสอบ Email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!formData.email.trim()) {
newErrors.email = "กรุณากรอก Email";
} else if (!emailRegex.test(formData.email)) {
newErrors.email = "รูปแบบ Email ไม่ถูกต้อง";
}
#6. ตรวจสอบข้อมูลตอน Submit
ตัวอย่าง
const handleSubmit = (e) => {
e.preventDefault();
if (!validate()) {
return;
}
console.log("Form Data:", formData);
};
e.preventDefault() ใช้ป้องกัน Browser Reload Page เมื่อ Submit Form
#7. ตัวอย่าง Complete Form
import { useState } from "react";
function RegisterForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const validate = () => {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = "กรุณากรอกชื่อ";
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!formData.email.trim()) {
newErrors.email = "กรุณากรอก Email";
} else if (!emailRegex.test(formData.email)) {
newErrors.email = "รูปแบบ Email ไม่ถูกต้อง";
}
if (!formData.password) {
newErrors.password = "กรุณากรอก Password";
} else if (formData.password.length < 8) {
newErrors.password = "Password ต้องมีอย่างน้อย 8 ตัวอักษร";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (!validate()) {
return;
}
console.log("Submit:", formData);
alert("สมัครสมาชิกสำเร็จ");
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>ชื่อ</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
{errors.name && (
<p style={{ color: "red" }}>
{errors.name}
</p>
)}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && (
<p style={{ color: "red" }}>
{errors.email}
</p>
)}
</div>
<div>
<label>Password</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && (
<p style={{ color: "red" }}>
{errors.password}
</p>
)}
</div>
<button type="submit">
Register
</button>
</form>
);
}
export default RegisterForm;
#8. Validation แบบ Real-time
บางระบบต้องการตรวจสอบข้อมูลทันทีขณะที่ผู้ใช้พิมพ์
สามารถสร้าง Function เช่น
const validateField = (name, value) => {
let error = "";
if (name === "email") {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!value.trim()) {
error = "กรุณากรอก Email";
} else if (!emailRegex.test(value)) {
error = "Email ไม่ถูกต้อง";
}
}
if (name === "password") {
if (!value) {
error = "กรุณากรอก Password";
} else if (value.length < 8) {
error = "Password ต้องมีอย่างน้อย 8 ตัวอักษร";
}
}
setErrors((prev) => ({
...prev,
[name]: error,
}));
};
แล้วเรียกใน handleChange
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
validateField(name, value);
};
Flow จะกลายเป็น
Typing
↓
onChange
↓
Update State
↓
validateField()
↓
Update Error
↓
UI แสดงผลทันที
#9. Validation ตอน onBlur
อีกแนวทางคือ Validation เมื่อผู้ใช้ออกจาก Input
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
onBlur={(e) =>
validateField(e.target.name, e.target.value)
}
/>
แนวทางนี้ช่วยลดข้อความ Error ที่ปรากฏเร็วเกินไปในขณะที่ผู้ใช้ยังพิมพ์ไม่เสร็จ
#10. เพิ่ม Touched State
ในฟอร์มที่ต้องการ UX ที่ดีขึ้น สามารถเก็บว่า Field ใดเคยถูกใช้งานแล้ว
const [touched, setTouched] = useState({});
สร้าง Function
const handleBlur = (e) => {
const { name } = e.target;
setTouched((prev) => ({
...prev,
[name]: true,
}));
};
จากนั้นแสดง Error เฉพาะ Field ที่ผู้ใช้เคยแตะ
{touched.email && errors.email && (
<p>{errors.email}</p>
)}
#11. ตัวอย่าง Validation หลายเงื่อนไข
#Username
if (!formData.username.trim()) {
newErrors.username = "กรุณากรอก Username";
} else if (formData.username.length < 4) {
newErrors.username = "Username ต้องมีอย่างน้อย 4 ตัวอักษร";
}
#Password
if (!formData.password) {
newErrors.password = "กรุณากรอก Password";
} else if (formData.password.length < 8) {
newErrors.password = "Password ต้องมีอย่างน้อย 8 ตัวอักษร";
}
#Confirm Password
if (!formData.confirmPassword) {
newErrors.confirmPassword = "กรุณายืนยัน Password";
} else if (
formData.confirmPassword !== formData.password
) {
newErrors.confirmPassword = "Password ไม่ตรงกัน";
}
#Age
if (!formData.age) {
newErrors.age = "กรุณากรอกอายุ";
} else if (Number(formData.age) < 18) {
newErrors.age = "อายุต้องไม่น้อยกว่า 18 ปี";
}
#12. Checkbox Validation
ตัวอย่าง Terms and Conditions
State
const [formData, setFormData] = useState({
email: "",
acceptTerms: false,
});
จัดการ Checkbox
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData((prev) => ({
...prev,
[name]: type === "checkbox" ? checked : value,
}));
};
Validation
if (!formData.acceptTerms) {
newErrors.acceptTerms =
"กรุณายอมรับเงื่อนไขการใช้งาน";
}
#13. Select Validation
<select
name="role"
value={formData.role}
onChange={handleChange}
>
<option value="">เลือก Role</option>
<option value="student">Student</option>
<option value="teacher">Teacher</option>
</select>
Validation
if (!formData.role) {
newErrors.role = "กรุณาเลือก Role";
}
#14. Disable Submit Button
สามารถปิดปุ่ม Submit หาก Form ยังไม่สมบูรณ์
const isFormValid =
formData.name.trim() &&
formData.email.trim() &&
formData.password.length >= 8;
จากนั้น
<button
type="submit"
disabled={!isFormValid}
>
Register
</button>
#15. Validation ฝั่ง Client ไม่เพียงพอ
แม้ว่า React จะ Validate ข้อมูลแล้ว แต่ Server ยังต้อง Validate ซ้ำเสมอ
โครงสร้างที่เหมาะสมคือ
React Form
↓
Client Validation
↓
API Request
↓
Server Validation
↓
Database
เหตุผลคือ Client-side Validation สามารถถูกข้ามได้ เช่น ผู้ใช้เรียก API โดยตรงผ่าน Postman, curl หรือ Script
ดังนั้น Validation ฝั่ง Server ยังคงเป็นข้อบังคับด้านความถูกต้องและความปลอดภัย
#16. React State Validation เหมาะกับงานแบบไหน
React State Validation เหมาะกับ
- Form ขนาดเล็ก
- Login Form
- Contact Form
- Search Form
- Registration Form ที่มี Field ไม่มาก
- Workshop สำหรับเรียนรู้ React State
- Prototype หรือ MVP
หาก Form มีความซับซ้อน เช่น
- หลายสิบ Field
- Nested Object
- Dynamic Form
- Field Array
- Multi-step Form
- Validation Rule จำนวนมาก
ควรพิจารณาใช้ Library เช่น
React Hook Form
+
Zod / Yup
เพราะช่วยลด Boilerplate Code และจัดการ Validation Schema ได้เป็นระบบมากกว่า
#17. เปรียบเทียบแนวทาง
| แนวทาง | เหมาะกับ | จุดเด่น |
|---|---|---|
| React State | Form เล็ก | เข้าใจง่าย ไม่ต้องติดตั้ง Library |
| React Hook Form | Form กลาง-ใหญ่ | Performance ดี Boilerplate ต่ำ |
| Formik | Form ทั่วไป | API เข้าใจง่าย มี ecosystem |
| Zod | Schema Validation | TypeScript Friendly |
| Yup | Schema Validation | ใช้งานร่วมกับ Form Library ได้ดี |
#18. Best Practices
#1. แยก Validation Logic
แทนที่จะเขียนทุกอย่างใน handleSubmit
const validate = () => {
// validation rules
};
ทำให้ Code อ่านง่ายและ Test ได้ง่ายกว่า
#2. ใช้ Functional State Update
setFormData((prev) => ({
...prev,
[name]: value,
}));
ช่วยให้ปลอดภัยกว่าเมื่อต้อง Update State จากค่าก่อนหน้า
#3. ไม่แสดง Error เร็วเกินไป
สำหรับ UX ที่ดีควรใช้
onBlur
+
touched state
แทนการแสดง Error ทุกครั้งที่ผู้ใช้เริ่มพิมพ์
#4. Validate ทั้ง Client และ Server
Client Validation มีหน้าที่ช่วย UX
Server Validation มีหน้าที่รักษาความถูกต้องและความปลอดภัยของข้อมูล
#สรุป
การทำ Form Validation ด้วย React State สามารถใช้ useState จัดการข้อมูลหลัก 3 ส่วน
formData
errors
touched
กระบวนการหลักคือ
User Input
↓
handleChange
↓
formData State
↓
Validation
↓
errors State
↓
Display Error
↓
Submit
สำหรับฟอร์มขนาดเล็ก การใช้ React State เป็นวิธีที่ตรงไปตรงมาและช่วยให้เข้าใจกลไก Controlled Component ของ React ได้เป็นอย่างดี
แต่หากระบบมี Form จำนวนมากหรือ Validation ซับซ้อน ควรพิจารณาใช้ React Hook Form + Zod เพื่อให้ Code ดูแลรักษาง่ายและรองรับการขยายระบบได้ดีกว่า