ทำความรู้จักกับ useEffect และ Side Effects
เวลาเราเขียนเว็บด้วย React (ไลบรารีสำหรับสร้างหน้าจอผู้ใช้) บางครั้งเราต้องทำอะไรที่อยู่นอกเหนือการแสดงผลปกติ เช่น การดึงข้อมูลจากเซิร์ฟเวอร์ หรือการตั้งเวลาแจ้งเตือน งานพวกนี้เราเรียกว่า Side Effects (งานที่ส่งผลกระทบต่อภายนอกระบบ) เปรียบเหมือนเวลาเราทำอาหาร ถ้าแค่หั่นผักใส่จานนั่นคือการแสดงผล แต่ถ้าเราต้องเปิดเตาแก๊สหรือล้างจานเพิ่ม นั่นคือ Side Effects ที่เกิดขึ้นระหว่างทาง
ใน React เราใช้ฟังก์ชันที่ชื่อว่า useEffect (ฟังก์ชันสำหรับจัดการงานที่ต้องทำเมื่อสถานะเปลี่ยน) เพื่อจัดการงานพวกนี้ ถ้าเราไม่ใช้มันอย่างถูกต้อง โปรแกรมอาจจะทำงานซ้ำไปซ้ำมาไม่หยุด จนทำให้เว็บค้างหรือทำงานช้าได้ การเข้าใจวิธีใช้ useEffect จึงเป็นทักษะสำคัญที่มือใหม่ทุกคนต้องมีเพื่อสร้างแอปที่เสถียร
เมื่อเราใช้ร่วมกับ TypeScript (ภาษาที่ช่วยระบุประเภทข้อมูลให้โค้ด) เราจะสามารถควบคุมข้อมูลที่ส่งเข้าและออกจาก useEffect ได้แม่นยำขึ้น การระบุ Type (ชนิดข้อมูล) จะช่วยลดบั๊ก (ข้อผิดพลาดของโปรแกรม) ที่เกิดจากการพิมพ์ผิดหรือใช้ข้อมูลผิดประเภท ทำให้เราทำงานได้เร็วขึ้นและมั่นใจกว่าเดิม
import { useEffect } from 'react';
// ตัวอย่างการใช้งาน useEffect เบื้องต้น
const MyComponent = () => {
useEffect(() => {
console.log("ทำงานทุกครั้งที่คอมโพเนนต์แสดงผล");
});
return <div>สวัสดีครับ</div>;
};
การจัดการ Dependency Array ให้ถูกจุด
Dependency Array (อาร์เรย์รายการตัวแปรที่ต้องเฝ้าสังเกต) คือช่องว่างด้านหลัง useEffect ที่เอาไว้ใส่ตัวแปรที่เราต้องการให้มันคอยตรวจสอบ ถ้าตัวแปรในนี้เปลี่ยนไป useEffect ถึงจะทำงานอีกครั้ง เปรียบเหมือนเซนเซอร์ไฟอัตโนมัติที่จะเปิดไฟก็ต่อเมื่อมีคนเดินผ่าน ถ้าไม่มีใครเดินผ่าน ไฟก็ไม่ต้องเปิดให้เปลืองพลังงาน
ถ้าเราปล่อยช่องนี้ว่างไว้ (ไม่ใส่อะไรเลย) useEffect จะทำงานทุกครั้งที่มีการอัปเดตหน้าจอ ซึ่งมักจะไม่ใช่สิ่งที่เราต้องการ ถ้าเราใส่ [] (อาร์เรย์ว่าง) มันจะทำงานแค่ตอนที่หน้าจอโหลดขึ้นมาครั้งแรกเท่านั้น การเลือกใส่ให้ถูกคือหัวใจสำคัญของการเขียนโปรแกรมที่มีประสิทธิภาพ
มือใหม่มักพลาดโดยการลืมใส่ตัวแปรที่ใช้งานจริงลงไปใน Dependency Array ทำให้ค่าที่แสดงผลบนหน้าจอไม่ยอมอัปเดตตามความเป็นจริง วิธีเช็กง่ายๆ คือ ให้ใส่ตัวแปรทุกตัวที่เราเรียกใช้ใน useEffect ลงไปในอาร์เรย์นี้ แล้วโปรแกรมจะทำงานได้ถูกต้องตามที่เราคาดหวังไว้เสมอ
import { useEffect, useState } from 'react';
const UserProfile = ({ userId }: { userId: number }) => {
useEffect(() => {
console.log("ดึงข้อมูลผู้ใช้ใหม่เมื่อ userId เปลี่ยน:", userId);
}, [userId]); // ใส่ userId ลงไป เพื่อให้มันทำงานใหม่เฉพาะตอน id เปลี่ยน
return <div>แสดงข้อมูลผู้ใช้ {userId}</div>;
};
การทำ Cleanup Function เพื่อคืนค่าทรัพยากร
Cleanup Function (ฟังก์ชันสำหรับล้างข้อมูลเก่า) คือฟังก์ชันที่เราเขียนไว้ข้างใน useEffect เพื่อยกเลิกงานที่ค้างอยู่ก่อนที่ useEffect จะเริ่มทำงานรอบใหม่ หรือตอนที่คอมโพเนนต์ถูกลบออกไปแล้ว เปรียบเหมือนการปิดไฟและล็อกห้องก่อนออกจากบ้าน ถ้าเราไม่ทำความสะอาดให้เรียบร้อย อาจจะเกิดปัญหาข้อมูลค้างในระบบได้
เราสร้างมันได้ง่ายๆ โดยการใช้คำสั่ง return แล้วตามด้วยฟังก์ชันข้างใน useEffect อีกทีหนึ่ง มันมีประโยชน์มากเวลาเราทำระบบแจ้งเตือนที่ต้องตั้งเวลาไว้ หรือการดึงข้อมูลที่อาจจะยังไม่เสร็จแล้วผู้ใช้กดเปลี่ยนหน้าไปก่อน ถ้าเราไม่ Cleanup (ล้างข้อมูลเก่า) อาจเกิดการอัปเดตค่าในคอมโพเนนต์ที่ไม่มีอยู่จริงจนโปรแกรมพังได้
หลายคนมักละเลยขั้นตอนนี้เพราะคิดว่าแค่เขียนโค้ดให้ทำงานได้ก็พอ แต่ในโลกของการทำงานจริง การจัดการหน่วยความจำ (พื้นที่เก็บข้อมูลชั่วคราวของโปรแกรม) เป็นเรื่องสำคัญมาก การหัดเขียน Cleanup Function จะช่วยให้แอปพลิเคชันของคุณดูเป็นมืออาชีพและไม่กินทรัพยากรเครื่องของผู้ใช้เกินความจำเป็น
useEffect(() => {
const timer = setInterval(() => {
console.log("ทำงานทุกๆ 1 วินาที");
}, 1000);
// ทำการล้างเวลาเมื่อคอมโพเนนต์ถูกเอาออกไป
return () => clearInterval(timer);
}, []);
ระบุ Type ตัวแปรภายใน Effect
เมื่อเราใช้ TypeScript สิ่งที่สำคัญที่สุดคือการบอกให้โปรแกรมรู้ว่าข้อมูลที่เราจัดการอยู่มีหน้าตาเป็นอย่างไร การระบุ Type ให้ตัวแปรภายใน useEffect จะช่วยให้เราเข้าถึงคุณสมบัติของข้อมูลได้โดยไม่มีข้อผิดพลาด เปรียบเหมือนการแปะป้ายชื่อหน้ากล่องพัสดุ ทำให้เราหยิบของใช้ได้ถูกกล่องโดยไม่ต้องแกะดูข้างในก่อน
เวลาเราดึงข้อมูลจาก API (ช่องทางเชื่อมต่อข้อมูลระหว่างเว็บกับเซิร์ฟเวอร์) เราควรสร้าง interface (รูปแบบโครงสร้างของข้อมูล) ขึ้นมารับค่าเสมอ เพื่อให้ TypeScript ช่วยตรวจสอบว่าเราพิมพ์ชื่อตัวแปรผิดไหม หรือข้อมูลที่ได้มามีค่าว่างหรือไม่ ถ้าเราไม่ระบุ Type ให้ชัดเจน เราอาจจะเผลอเรียกใช้ตัวแปรที่ไม่มีอยู่จริงจนหน้าเว็บขาวโพลนได้
การฝึกเขียน Type ให้ติดเป็นนิสัยจะทำให้คุณก้าวข้ามจากการเป็นมือใหม่สู่ระดับโปรแกรมเมอร์มืออาชีพได้เร็วขึ้น เพราะมันจะช่วยลดเวลาในการไล่หาบั๊ก (การหาจุดผิดพลาดของโค้ด) ได้มหาศาล ยิ่งงานในทีมมีขนาดใหญ่เท่าไหร่ ความชัดเจนของ Type ยิ่งช่วยให้คนอื่นในทีมเข้าใจโค้ดของคุณได้ง่ายขึ้นเท่านั้น
interface User {
id: number;
name: string;
}
useEffect(() => {
const fetchUser = async () => {
const response = await fetch('/api/user');
const data: User = await response.json(); // ระบุ Type ให้ข้อมูลที่ได้มา
console.log(data.name);
};
fetchUser();
}, []);
จุดที่มือใหม่มักพลาดและวิธีแก้ไข
ปัญหาที่เจอบ่อยที่สุดคือการใส่ตัวแปรผิดประเภทลงใน Dependency Array จนทำให้เกิดการทำงานไม่รู้จบ หรือที่เรียกว่า Infinite Loop (การทำงานวนซ้ำไม่สิ้นสุด) เปรียบเหมือนการที่เราตั้งเงื่อนไขว่า "ถ้าไฟเปิด ให้เปิดไฟ" มันจะวนลูปจนค่าไฟพุ่งและระบบรวน วิธีแก้คือต้องตรวจดูเสมอว่าตัวแปรในอาร์เรย์นั้นเป็นค่าคงที่หรือค่าที่เปลี่ยนแปลงได้จริงๆ
อีกจุดคือการลืมจัดการกับ Async/Await (คำสั่งจัดการงานที่ใช้เวลานาน) ภายใน useEffect โดยตรง เพราะ useEffect ไม่รองรับฟังก์ชันแบบ Async โดยตรง วิธีที่ถูกต้องคือการสร้างฟังก์ชันแยกไว้ข้างในแล้วค่อยเรียกใช้ หรือใช้การจัดการสถานะให้ดีขึ้นเพื่อป้องกันข้อมูลไหลเข้ามาระหว่างที่คอมโพเนนต์กำลังทำงาน
สุดท้ายคือการไม่เช็กสถานะก่อนอัปเดตข้อมูล ถ้าคอมโพเนนต์ถูกทำลายไปแล้ว (ถูกเอาออกจากหน้าจอ) แต่เรายังพยายามอัปเดตค่าอยู่ จะเกิดข้อผิดพลาดขึ้นเสมอ ให้ใช้ตัวแปรแบบ boolean (ค่าจริงหรือเท็จ) เข้ามาช่วยเช็กสถานะว่าคอมโพเนนต์ยังทำงานอยู่หรือไม่ก่อนจะสั่งอัปเดตค่าใดๆ ลงไปในหน้าจอ
- สร้างฟังก์ชันภายใน
useEffectเพื่อจัดการงานที่ต้องรอเวลา - ระบุ Type ให้ข้อมูลที่ได้รับมาเพื่อป้องกันการเข้าถึงค่าผิดพลาด
- ใส่ Dependency Array ให้ครบถ้วนตามตัวแปรที่เรียกใช้
- เขียน Cleanup Function เพื่อคืนค่าทรัพยากรทุกครั้งที่มีการใช้ตัวจับเวลา
สรุปและแนวทางการฝึกฝน
การใช้ useEffect และ TypeScript อาจดูยากในตอนแรก แต่มันเป็นรากฐานสำคัญของการพัฒนาเว็บสมัยใหม่ เริ่มต้นให้ลองสร้างโปรเจกต์เล็กๆ เช่น แอปนาฬิกาจับเวลา หรือแอปดึงข้อมูลรายชื่อเพื่อนมาฝึกฝน การลงมือทำจริงจะทำให้คุณเห็นภาพว่า Dependency Array และ Cleanup Function มีผลอย่างไรในสถานการณ์จริง
จำไว้ว่าโปรแกรมเมอร์ที่เก่งไม่ได้เก่งเพราะจำคำสั่งได้ทั้งหมด แต่เก่งเพราะรู้วิธีจัดการกับปัญหาและเครื่องมือที่ใช้ ถ้าคุณเจอบั๊ก อย่าเพิ่งท้อ ให้ใช้เครื่องมืออย่าง console.log เพื่อดูค่าที่ส่งเข้ามาใน useEffect ทีละขั้นตอน การทำความเข้าใจแต่ละบรรทัดที่คุณเขียนคือเส้นทางสู่การเป็นมืออาชีพที่แท้จริง
หัวใจสำคัญคือความสม่ำเสมอ ลองฝึกเขียนโค้ดวันละ 1 ชั่วโมง โดยเน้นไปที่การลองผิดลองถูกกับ useEffect ให้บ่อยที่สุด เมื่อคุณเริ่มคุ้นเคย คุณจะพบว่าการเขียนโค้ดที่สะอาดและปลอดภัยด้วย TypeScript ไม่ใช่เรื่องน่ากลัวอีกต่อไป ขอให้สนุกกับการเขียนโค้ดครับ!