ทำความรู้จักกับ useEffect และเหตุผลที่ต้องมี Cleanup Function
ในการเขียนโปรแกรมด้วย React 19 เรามักจะต้องทำงานที่เกี่ยวข้องกับ Side Effect (ผลข้างเคียง) เช่น การดึงข้อมูลจากฐานข้อมูล หรือการจัดการกับ Timer ต่างๆ ซึ่ง useEffect คือเครื่องมือหลักที่ React เตรียมไว้ให้เราจัดการสิ่งเหล่านี้ โดยเปรียบเสมือน "งานที่ต้องทำหลังจากหน้าจอแสดงผลเสร็จแล้ว" เพื่อให้แอปพลิเคชันของเราทำงานได้อย่างต่อเนื่องและไม่ติดขัด
อย่างไรก็ตาม ปัญหาที่มือใหม่มักเจอคือ Memory Leak หรือการที่โปรแกรมจองหน่วยความจำไว้แล้วไม่ยอมคืน ทำให้แอปฯ ทำงานช้าลงเรื่อยๆ ลองนึกภาพว่าคุณเปิดไฟในห้องทิ้งไว้ทุกครั้งที่เดินเข้าห้อง แต่ไม่เคยปิดเลย สุดท้ายบ้านทั้งหลังก็จะเต็มไปด้วยไฟที่เปิดค้างไว้จนเปลืองพลังงาน Cleanup Function จึงเปรียบเสมือนการเดินไปปิดสวิตช์ไฟทุกครั้งที่คุณเดินออกจากห้องนั่นเองครับ
การเข้าใจเรื่องนี้เป็นทักษะสำคัญของ Software Developer ที่ดี เพราะมันบ่งบอกว่าคุณไม่ได้แค่เขียนโค้ดให้ทำงานได้ แต่คุณใส่ใจถึงประสิทธิภาพของซอฟต์แวร์ด้วย การเขียน useEffect ที่ถูกต้องจะช่วยให้แอปพลิเคชันของคุณมีความเสถียร ไม่เกิดข้อผิดพลาดแปลกๆ เวลาที่ผู้ใช้งานสลับหน้าจอไปมา หรือปิดหน้าเว็บแบบกะทันหัน
useEffect(() => {
// งานที่ต้องการทำ (เช่น การดึงข้อมูล หรือ Set Interval)
const timer = setInterval(() => console.log("ทำงานอยู่..."), 1000);
// Cleanup Function: ทำงานเมื่อ Component ถูกทำลาย
return () => {
clearInterval(timer); // คืนหน่วยความจำด้วยการหยุด Timer
console.log("ปิดการทำงานและคืน Memory แล้ว");
};
}, []); // Dependency Array ว่างๆ หมายถึงรันครั้งเดียวตอนเริ่ม
การดึงข้อมูลด้วย fetch และ axios พื้นฐานสำหรับมือใหม่
หัวใจสำคัญของการทำแอปฯ เว็บสมัยใหม่คือการดึงข้อมูลจาก API (ช่องทางที่ให้โปรแกรมหนึ่งคุยกับอีกโปรแกรมหนึ่ง) มาแสดงผลให้ผู้ใช้เห็น โดยเรานิยมใช้เครื่องมืออย่าง fetch ซึ่งเป็นฟังก์ชันมาตรฐานที่ติดมากับเบราว์เซอร์ หรือ axios ซึ่งเป็น Library (ชุดคำสั่งสำเร็จรูป) ยอดนิยมที่ช่วยให้การดึงข้อมูลทำได้ง่ายและจัดการข้อผิดพลาดได้ดีกว่า
สมมติว่าคุณต้องการดึงรายชื่อผู้ใช้งานจากเซิร์ฟเวอร์มาแสดง คุณต้องใช้ useEffect เป็นตัวสั่งการหลัก เพราะเราไม่สามารถดึงข้อมูลแบบสุ่มสี่สุ่มห้าได้ แต่ต้องรอให้หน้าเว็บโหลดเสร็จก่อน การจัดการสถานะ (State) ใน React จึงมีความสำคัญมาก เพราะคุณต้องเก็บข้อมูลที่ได้มาไว้ในตัวแปร เพื่อให้ React นำไปวาดหน้าจอ (Render) ต่อไป
ข้อควรระวังคือการดึงข้อมูลซ้ำซ้อนโดยไม่จำเป็น ซึ่งอาจทำให้เซิร์ฟเวอร์ทำงานหนักหรือเว็บค้างได้ ดังนั้นการทำความเข้าใจเรื่อง Dependency Array (ตัวกำหนดเงื่อนไขการรัน) ใน useEffect จึงเป็นเรื่องที่โปรแกรมเมอร์จูเนียร์ต้องแม่นยำ เพื่อไม่ให้แอปฯ ของคุณเรียกข้อมูลทุกครั้งที่หน้าจอมีการขยับตัวเพียงเล็กน้อย
import { useState, useEffect } from 'react';
// ตัวอย่างการใช้ fetch ดึงข้อมูล
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => setUsers(data));
}, []); // รันแค่รอบเดียวตอน Component แสดงผลครั้งแรก
return <div>{users.length} คนในระบบ</div>;
}
การจัดการ Memory Leak เมื่อข้อมูลมาถึงช้ากว่าการปิดหน้าจอ
ปัญหาคลาสสิกที่คนเริ่มเขียน React มักเจอคือ "การพยายามอัปเดตสถานะใน Component ที่ถูกทำลายไปแล้ว" ตัวอย่างเช่น คุณสั่งให้เว็บไปดึงข้อมูล แต่ผู้ใช้ดันกดเปลี่ยนหน้าเว็บหนีไปก่อนที่ข้อมูลจะเดินทางมาถึง ทำให้โปรแกรมพยายามเอาข้อมูลไปใส่ในหน้าเว็บที่ไม่มีอยู่จริง ส่งผลให้เกิด Warning ใน Console ว่า Memory Leak
วิธีแก้ปัญหาคือการใช้ตัวแปรแบบ Flag (ธงสถานะ) เพื่อเช็คว่า Component ยังเปิดอยู่หรือไม่ ก่อนที่จะสั่งให้โปรแกรมทำงานต่อ หากหน้าเว็บถูกปิดไปแล้ว เราก็แค่สั่งให้ฟังก์ชันหยุดทำงานทันทีโดยไม่ต้องอัปเดต State ใดๆ เปรียบเหมือนการที่คุณส่งจดหมายไปหาเพื่อน แต่เพื่อนย้ายบ้านไปแล้ว คุณก็แค่เก็บจดหมายนั้นไว้ ไม่ต้องพยายามยัดเยียดเข้าตู้จดหมายที่ไม่มีคนอยู่
เทคนิคนี้จะทำให้โค้ดของคุณดูเป็นมืออาชีพและจัดการทรัพยากรได้อย่างชาญฉลาด การเขียนโปรแกรมไม่ได้มีแค่เรื่องของฟังก์ชัน แต่เป็นเรื่องของการสื่อสารระหว่างโปรแกรมและทรัพยากรเครื่อง การฝึกเขียน Cleanup Function ให้เป็นนิสัยจะช่วยให้คุณลดบั๊กที่หาสาเหตุยากๆ ไปได้มหาศาลเมื่อต้องทำโปรเจกต์ขนาดใหญ่
useEffect(() => {
let isMounted = true; // Flag เช็คสถานะการเปิดใช้งาน
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => {
if (isMounted) { // ถ้า Component ยังเปิดอยู่ถึงจะเซ็ตค่า
setData(data);
}
});
return () => { isMounted = false; }; // ถ้าปิดหน้าเว็บ ให้เปลี่ยน Flag
}, []);
ความแตกต่างระหว่าง fetch กับ axios ที่มือใหม่ควรรู้
หลายคนมักถามว่าควรเลือกใช้ fetch หรือ axios ดี คำตอบคือทั้งคู่ทำหน้าที่เหมือนกันคือการติดต่อ API แต่ fetch เป็นฟังก์ชันพื้นฐานที่เบราว์เซอร์ให้มาโดยตรง ส่วน axios เป็นไลบรารีภายนอกที่ถูกออกแบบมาให้ใช้งานสะดวกกว่า เช่น การแปลงข้อมูล JSON ให้โดยอัตโนมัติ หรือการจัดการข้อผิดพลาด (Error Handling) ที่ชัดเจนกว่า
สำหรับคนที่กำลังฝึกเขียนโปรแกรม การเริ่มด้วย fetch จะช่วยให้คุณเข้าใจหลักการทำงานของ HTTP Request (การร้องขอข้อมูล) ได้ดีขึ้น แต่เมื่อคุณต้องทำโปรเจกต์จริงในบริษัทหรือในทีม การใช้ axios มักจะเป็นมาตรฐานที่ยอมรับกันมากกว่า เพราะความสามารถในการจัดการ Interceptors (ตัวดักจับคำสั่ง) ซึ่งช่วยให้คุณใส่ Token หรือจัดการ Header ได้ในจุดเดียวสำหรับทุกคำสั่ง
ไม่ว่าคุณจะเลือกใช้อะไร สิ่งสำคัญคือการเข้าใจว่าทั้งสองอย่างนี้ทำงานแบบ Asynchronous (ทำงานไม่พร้อมกัน) นั่นหมายความว่าโปรแกรมจะไม่รอให้ข้อมูลมาถึงก่อนแล้วค่อยทำบรรทัดถัดไป แต่จะรันบรรทัดถัดไปทันที การใช้ async / await จึงเป็นวิธีที่ช่วยให้โค้ดของคุณอ่านง่ายและเป็นระเบียบมากขึ้นกว่าการใช้ .then() แบบเดิมๆ
// เปรียบเทียบการเขียนด้วย async/await ให้ดูสะอาดตา
async function fetchData() {
try {
const response = await fetch('https://api.example.com');
const data = await response.json();
console.log(data);
} catch (error) {
console.error("เกิดข้อผิดพลาด:", error);
}
}
วินัยและทัศนคติ: การฝึกฝนเพื่อก้าวสู่โปรแกรมเมอร์มืออาชีพ
การเรียนรู้เรื่อง useEffect หรือการดึงข้อมูลอาจดูเป็นเรื่องยากในช่วงแรก แต่โปรแกรมเมอร์ที่เก่งไม่ได้เก่งเพราะจำคำสั่งได้ทั้งหมด แต่เก่งเพราะ "รู้วิธีแก้ปัญหา" เมื่อเจอบั๊ก อย่าเพิ่งท้อแท้หรือรีบก๊อปปี้โค้ดจาก Stack Overflow มาวาง ให้ลองอ่าน Error Message ใน Console ดูว่ามันพยายามบอกอะไรเรา และค่อยๆ วิเคราะห์ทีละบรรทัด
สมมติว่าคุณฝึกเขียนโค้ดวันละ 1-2 ชั่วโมงต่อวัน คุณควรแบ่งเวลาให้กับการทำ Mini-Project เสมอ เช่น การสร้างเว็บแสดงสภาพอากาศ หรือเว็บค้นหาหนัง เพราะทฤษฎีอย่างเดียวจะทำให้คุณจำได้แค่ชั่วคราว แต่การทำโปรเจกต์จะทำให้คุณเจอปัญหาจริง ซึ่งเป็นจุดที่ทักษะของคุณจะก้าวกระโดดได้เร็วที่สุด การเป็นโปรแกรมเมอร์คือการเรียนรู้ตลอดชีวิต
จงจำไว้ว่าไม่มีใครเขียนโค้ดสมบูรณ์แบบตั้งแต่ครั้งแรก มืออาชีพทุกคนต่างเคยผ่านจุดที่ Memory Leak หรือบั๊กพังทั้งแอปฯ มาแล้วทั้งนั้น สิ่งที่แยกคนเก่งออกจากคนทั่วไปคือ "ความอดทนในการไล่แก้บั๊ก" และ "วินัยในการฝึกฝน" หากคุณทำตามขั้นตอนและฝึกฝนอย่างต่อเนื่อง การเป็นโปรแกรมเมอร์ที่เก่งกาจก็ไม่ใช่เรื่องไกลตัวเลยครับ
สรุป: การนำไปใช้จริงในโปรเจกต์ของคุณ
สรุปสั้นๆ สำหรับการนำความรู้ไปใช้ คือ 1. ทุกครั้งที่ใช้ useEffect ให้ถามตัวเองว่าจำเป็นต้องมี Cleanup Function หรือไม่ 2. เลือกใช้ fetch หรือ axios ตามความเหมาะสมของโปรเจกต์ และ 3. หมั่นเช็คสถานะการทำงานด้วย Flag เพื่อป้องกัน Memory Leak ที่จะทำให้เว็บของคุณช้าลงโดยไม่รู้ตัว
ตัวอย่างการนำไปใช้ในงานจริง เช่น หากคุณกำลังทำหน้า "Profile ผู้ใช้งาน" คุณควรใช้ useEffect เพื่อดึงข้อมูลโปรไฟล์จาก API ทันทีที่หน้าจอโหลด และหากผู้ใช้งานกดสลับไปยังเมนู "ตั้งค่า" อย่างรวดเร็ว Cleanup Function จะทำหน้าที่ยกเลิกคำสั่งดึงข้อมูลนั้นทิ้งทันที เพื่อไม่ให้โปรแกรมพยายามนำข้อมูลไปแสดงในหน้าจอที่ผู้ใช้ไม่ได้อยู่แล้ว
การเขียนโค้ดที่สะอาดและมีประสิทธิภาพแบบนี้ ไม่เพียงแต่ช่วยให้แอปฯ ของคุณทำงานได้ลื่นไหล แต่ยังแสดงถึงความเป็นมืออาชีพในการเขียนโปรแกรม ขอให้สนุกกับการทดลองเขียนโค้ด และอย่าลืมว่าทุกบรรทัดที่คุณพิมพ์ลงไป คือการลงทุนกับทักษะที่จะติดตัวคุณไปตลอดอาชีพการงานครับ