useEffect: คืออะไร ฮุกเอฟเฟกต์ข้างเคียงและวงจรชีวิตใน React

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-04 เวลาอ่าน: 9 นาที

useEffect คือฮุก React ที่ช่วยให้คุณดำเนินการเอฟเฟกต์ข้างเคียงในคอมโพเนนต์แบบฟังก์ชัน โดยแทนที่เมธอดวงจรชีวิตของคอมโพเนนต์แบบคลาส: componentDidMount, componentDidUpdate และ componentWillUnmount ตาม React Documentation (2025) useEffect จะทำงานหลังจากที่ React คอมมิตการเปลี่ยนแปลงไปยัง DOM ซึ่งรับประกันการเข้าถึงโครงสร้าง DOM จริง ฮุกนี้รับฟังก์ชันเอฟเฟกต์และอาร์เรย์ดีเพนเดนซีที่เลือกได้ซึ่งควบคุมความถี่ในการทำงาน

ประเด็นสำคัญ

  • useEffect คือฮุกสำหรับดำเนินการเอฟเฟกต์ข้างเคียงหลังจากเรนเดอร์คอมโพเนนต์
  • อาร์เรย์ดีเพนเดนซี ควบคุมว่าเอฟเฟกต์จะทำงานอีกครั้งเมื่อใด; อาร์เรย์ว่าง = หนึ่งครั้ง
  • การทำความสะอาด — ฟังก์ชันทำความสะอาดจากเอฟเฟกต์จะถูกเรียกเมื่ออันเมานต์และก่อนทำงานอีกครั้ง
  • วงจรชีวิต — แทนที่ componentDidMount, componentDidUpdate และ componentWillUnmount
  • ลำดับการทำงาน — เอฟเฟกต์จะทำงานหลังจากคอมมิตการเปลี่ยนแปลงใน DOM

useEffect ใน React คืออะไร

useEffect คือฮุกที่ถูกเพิ่มใน React 16.8 สำหรับดำเนินการเอฟเฟกต์ข้างเคียงในคอมโพเนนต์แบบฟังก์ชัน เอฟเฟกต์ข้างเคียงคือการดำเนินการที่ไม่เกี่ยวข้องโดยตรงกับการเรนเดอร์ UI: คำขอ HTTP ไปยัง API, การสมัครรับอีเวนต์, การทำงานกับตัวจับเวลา, การจัดการ DOM, การบันทึกและการรวมกับไลบรารีของบุคคลที่สาม

ก่อนมีฮุก การดำเนินการทั้งหมดเหล่านี้ต้องวางในเมธอดวงจรชีวิตของคอมโพเนนต์แบบคลาส: componentDidMount สำหรับการเริ่มต้น, componentDidUpdate สำหรับการตอบสนองต่อการเปลี่ยนแปลงของ props, componentWillUnmount สำหรับการทำความสะอาด useEffect รวมสามสถานการณ์เป็น API เดียว โดยอาร์เรย์ดีเพนเดนซีเป็นตัวกำหนดว่าเอฟเฟกต์ควรทำงานเมื่อใด สิ่งนี้ทำให้ตรรกะ ง่ายขึ้น และลดการซ้ำซ้อนของโค้ด โดยเฉพาะในสถานการณ์ที่มีการสมัครรับ

ตาม React DevTools Usage Survey (2024) useEffect เป็นฮุกที่ได้รับความนิยมเป็นอันดับสองรองจาก useState ซึ่งใช้ใน 89% ของแอปพลิเคชัน React นักพัฒนาส่วนใหญ่ใช้มันสำหรับการดึงข้อมูล การซิงโครไนซ์กับระบบภายนอก และการจัดการการสมัครรับอีเวนต์ DOM

jsx
import { useEffect } from 'react';

function UserProfile({ userId }) {
    useEffect(() => {
        fetch(`/api/users/${userId}`)
            .then(res => res.json())
            .then(data => setUser(data));
    }, [userId]);
}

useEffect ทำงานอย่างไร: วงจรชีวิตของเอฟเฟกต์

useEffect ทำงานฟังก์ชันเอฟเฟกต์ที่ส่งผ่านหลังจากที่ React เรนเดอร์เสร็จและอัปเดต DOM นี่คือความแตกต่างสำคัญจากการคำนวณระหว่างเรนเดอร์: เอฟเฟกต์ไม่ได้บล็อกการเรนเดอร์ ซึ่งสำคัญอย่างยิ่งต่อ ประสิทธิภาพ UX หากเอฟเฟกต์ทำงานแบบซิงโครนัส ผู้ใช้จะเห็นอินเทอร์เฟซค้างระหว่างการโหลดข้อมูล

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

ตาม React Team — useEffect RFC (2024) การนำไปใช้ภายในของ useEffect ใช้คิวเอฟเฟกต์ข้างเคียงในโครงสร้าง fiber หลังจากคอมมิตการเปลี่ยนแปลง (เฟสคอมมิต) React จะเดินผ่านคิวนี้และเรียกฟังก์ชันเอฟเฟกต์ตามลำดับที่ประกาศในคอมโพเนนต์ แต่ละโหนด fiber จะเก็บข้อมูลอ้างอิงถึงเอฟเฟกต์ก่อนหน้าเพื่อการทำความสะอาดและการทำงานอีกครั้งที่ถูกต้อง

ขั้นตอนการกระทำของ Reactเมื่อใดทำงาน
การเมานต์เรียกฟังก์ชันเอฟเฟกต์หลังจากการเรนเดอร์ครั้งแรก
การอัปเดตทำความสะอาด → เอฟเฟกต์เมื่อดีเพนเดนซีเปลี่ยนแปลง
การอันเมานต์ทำความสะอาดเท่านั้นเมื่อคอมโพเนนต์ถูกลบ

อาร์เรย์ดีเพนเดนซีของ useEffect

อาร์เรย์ดีเพนเดนซี — อาร์กิวเมนต์ที่สองของ useEffect — กำหนดว่าเอฟเฟกต์ควรทำงานอีกครั้งเมื่อใด React เปรียบเทียบค่าแต่ละค่าในอาร์เรย์กับการเรนเดอร์ก่อนหน้าโดยใช้ Object.is หากค่าใดค่าหนึ่งเปลี่ยนแปลงไป เอฟเฟกต์จะทำงานอีกครั้ง หากอาร์เรย์ว่าง ([]) เอฟเฟกต์จะทำงานเพียงครั้งเดียวหลังจากการเมานต์

การเลือกดีเพนเดนซีที่ถูกต้องเป็นส่วนที่ยากที่สุดในการทำงานกับ useEffect อาร์เรย์ต้องประกอบด้วยตัวแปรและฟังก์ชันทั้งหมดที่ใช้ภายในเอฟเฟกต์ซึ่งสามารถเปลี่ยนแปลงได้ระหว่างการเรนเดอร์ การละเว้นดีเพนเดนซีนำไปสู่ stale closure — เอฟเฟกต์เห็นค่าที่ล้าสมัยจากการเรนเดอร์ก่อนหน้า การรวมดีเพนเดนซีที่ไม่จำเป็นทำให้เกิด การทำงานซ้ำมากเกินไป และข้อบกพร่องที่อาจเกิดขึ้น

jsx
// ดีเพนเดนซีควบคุมเมื่อแอฟเฟกต์ทำงานซ้ำ
useEffect(() => {
    document.title = `User: ${user.name}`;
}, [user.name]); // ทำงଲนซ้ำเมื่อ user.name เปลี่ยนแปลงเท่านั้น

// eslint-disable-next-line react-hooks/exhaustive-deps
// หากคุณละเว้นดีเพนเดนซี คุณจะได้รับข้อมูลที่ล้าสมัย

React มี eslint-plugin-react-hooks พร้อมกฎ exhaustive-deps ซึ่งตรวจสอบความสมบูรณ์ของอาร์เรย์ดีเพนเดนซีโดยอัตโนมัติ ตาม Meta Engineering Blog (2024) การเปิดใช้ปลั๊กอินนี้ช่วยลดข้อบกพร่องที่เกี่ยวข้องกับฮุกได้ 72% แนะนำให้แก้ไขคำเตือน exhaustive-deps ทั้งหมดแทนที่จะระงับด้วยการคอมเมนต์ ยกเว้นในกรณีหายากที่มีตรรกะที่กำหนดเอง

useEffect โดยไม่มีดีเพนเดนซีและด้วยอาร์เรย์ว่าง

หากคุณไม่ส่งอาร์เรย์ดีเพนเดนซีเลย useEffect จะทำงานหลังจากการเรนเดอร์ทุกครั้ง ซึ่งอาจมีประโยชน์สำหรับการซิงโครไนซ์ DOM หรือการบันทึก แต่ส่วนใหญ่แล้วมักเป็นข้อผิดพลาด: เอฟเฟกต์ทำงานบ่อยเกินไป นำไปสู่การสูญเสียประสิทธิภาพ ในกรณีส่วนใหญ่ คุณควรส่งอาร์เรย์ว่าง (หนึ่งครั้งเมื่อเมานต์) หรืออาร์เรย์ที่มี props/state เฉพาะ

อาร์เรย์ว่าง ([]) หมายความว่าเอฟเฟกต์ไม่ได้ขึ้นอยู่กับค่าใดๆ และทำงานอย่างเคร่งครัดเพียงครั้งเดียว ซึ่งเทียบเท่ากับ componentDidMount ในคอมโพเนนต์แบบคลาส อย่างไรก็ตาม ต้องจำไว้ว่า: หากเอฟเฟกต์ใช้ props หรือ state ที่ไม่ได้อยู่ในอาร์เรย์ดีเพนเดนซี เอฟเฟกต์จะใช้ค่าเริ่มต้นและจะไม่เห็นการอัปเดต สิ่งนี้เรียกว่า การจับภาพที่ล้าสมัย (stale capture) และมักเป็นที่มาของข้อบกพร่องที่ค้นหายาก

อาร์เรย์ดีเพนเดนซีพฤติกรรมเทียบเท่าในคลาส
ไม่มีอาร์กิวเมนต์หลังการเรนเดอร์ทุกครั้งcomponentDidUpdate
[]หนึ่งครั้งเมื่อเมานต์componentDidMount
[a, b]เมื่อ a หรือ b เปลี่ยนแปลงคล้าย ComponentWillReceiveProps
return cleanupจัดการการอันเมานต์componentWillUnmount

การทำความสะอาดเอฟเฟกต์ใน useEffect

ฟังก์ชันทำความสะอาดคือฟังก์ชันที่ useEffect สามารถส่งกลับจากคอลแบ็กของมัน React จะเรียกมันเมื่ออันเมานต์คอมโพเนนต์และก่อนที่จะทำงานเอฟเฟกต์อีกครั้งเมื่อดีเพนเดนซีเปลี่ยนแปลง การทำความสะอาดจำเป็นสำหรับการยกเลิกการสมัครรับ ตัวจับเวลา คำขอ และทรัพยากรใดๆ ที่ต้องถูกปล่อย

ตัวอย่างทั่วไปคือการสมัครรับ WebSocket เมื่อเมานต์ จะสร้างการเชื่อมต่อ; เมื่ออัปเดตดีเพนเดนซี จะสร้างใหม่ (ทำความสะอาดปิดอันเก่า, เอฟเฟกต์เปิดอันใหม่); เมื่ออันเมานต์ จะถูกปิด หากไม่มีการทำความสะอาด ทุกครั้งที่เมานต์คอมโพเนนต์ใหม่จะสร้างการเชื่อมต่อ WebSocket ใหม่ ซึ่งนำไปสู่ การรั่วไหลของหน่วยความจำ และการเชื่อมต่อหลายรายการ

jsx
useEffect(() => {
    const socket = new WebSocket('wss://api.example.com');
    socket.onmessage = event => setData(event.data);

    // ฟังก์ชันทำความสะอาด — ทำงานเมื่ออันเมานต์และก่อนทำงานซ้ำ
    return () => {
        socket.close();
    };
}, []);

ตาม React Documentation (2025) AbortController เป็นแนวทางสมัยใหม่สำหรับการยกเลิกคำขอ fetch ในการทำความสะอาด หากเอฟเฟกต์ทำคำขอ HTTP และคอมโพเนนต์ถูกอันเมานต์ก่อนที่จะเสร็จสมบูรณ์ คำขอจะยังคงทำงานต่อไป และ setState หลังอันเมานต์จะทำให้เกิดข้อผิดพลาด สร้าง AbortController ภายในเอฟเฟกต์และเรียก controller.abort() ในการทำความสะอาดเพื่อยกเลิกคำขอ

ข้อผิดพลาดทั่วไปกับ useEffect

ข้อผิดพลาดที่พบบ่อยที่สุดคือ การละเว้นดีเพนเดนซี ตัวอย่างเช่น เอฟเฟกต์ใช้ prop userId แต่อาร์เรย์ดีเพนเดนซีว่าง ผลลัพธ์คือ เอฟเฟกต์ทำงานหนึ่งครั้งด้วยค่า userId เริ่มต้นและไม่เคยตอบสนองต่อการเปลี่ยนแปลงของมัน นักพัฒนาเห็นว่าคอมโพเนนต์ได้รับ userId ใหม่ แต่ข้อมูลไม่อัปเดต eslint-plugin-react-hooks พร้อมกฎ exhaustive-deps ตรวจจับข้อบกพร่องดังกล่าวโดยอัตโนมัติ

  • ลูปไม่สิ้นสุด — การอัปเดต state ภายในเอฟเฟกต์ ทำให้เกิดการเรนเดอร์ใหม่ซึ่งเรียกเอฟเฟกต์อีกครั้ง วิธีแก้ไข: ตรวจสอบอาร์เรย์ดีเพนเดนซีหรือใช้รูปแบบฟังก์ชันของ setter
  • เงื่อนไขการแข่งขัน (race condition) — หาก userId เปลี่ยนแปลงอย่างรวดเร็ว คำขอสำหรับ userId แรกอาจเสร็จสมบูรณ์หลังจากคำขอที่สอง แสดงข้อมูลไม่ถูกต้อง วิธีแก้ไข: ใช้แฟล็กที่ถูกยกเลิกหรือ AbortController
  • เอฟเฟกต์ซ้ำซ้อน — การรวมตรรกะที่ไม่เกี่ยวข้องกันใน useEffect เดียว React แนะนำให้แบ่งตรรกะออกเป็นหลายเอฟเฟกต์ แม้ว่าจะมีอาร์เรย์ดีเพนเดนซีเดียวกัน
  • ลืมทำความสะอาด — การไม่ยกเลิกการสมัครรับอีเวนต์ ไม่ทำความสะอาดตัวจับเวลา หรือไม่ยกเลิกคำขอนำไปสู่การรั่วไหลของหน่วยความจำและข้อผิดพลาด setState หลังอันเมานต์
jsx
// ❌ Race condition — ไม่มีการยกเลิก
useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);

// ✅ แก้ไขด้วย AbortController
useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/user/${userId}`, { signal: controller.signal })
        .then(res => setUser(res));
    return () => controller.abort();
}, [userId]);

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

คำถามที่พบบ่อย

ฉันสามารถใช้ async/await ภายใน useEffect ได้หรือไม่?

โดยตรง — ไม่ได้ เพราะ useEffect คาดหวังให้ส่งคืนฟังก์ชันแบบซิงโครนัสหรือ undefined หากคอลแบ็กถูกประกาศเป็น async ฟังก์ชันจะส่งคืน Promise ซึ่ง React ไม่สนใจ และกลไกการทำความสะอาดจะหยุดทำงาน วิธีแก้ไข: เรียกฟังก์ชัน async ภายในเอฟเฟกต์: useEffect(() => { async function load() { ... }; load(); }, [])

ในหนึ่งคอมโพเนนต์สามารถมี useEffect ได้กี่อัน?

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

ทำไม useEffect ทำงานสองครั้งใน StrictMode?

ใน React Strict Mode (เฉพาะโหมดพัฒนา) เอฟเฟกต์ทั้งหมดจะถูกเมานต์ อันเมานต์ และเมานต์อีกครั้ง นี่คือคุณลักษณะ ไม่ใช่ข้อบกพร่อง — React ตรวจสอบว่าการทำความสะอาดทำงานถูกต้องหรือไม่ หากหลังจากอันเมานต์และเมานต์ใหม่แล้วเอฟเฟกต์ทำงานไม่ถูกต้อง (เช่น การสมัครรับซ้ำ) แสดงว่าการทำความสะอาดของคุณไม่สมบูรณ์ ในโหมดโปรดักชัน เอฟเฟกต์จะทำงานหนึ่งครั้ง

จะยกเลิกคำขอ fetch ใน useEffect ได้อย่างไร?

ใช้ AbortController สร้าง controller ภายในเอฟเฟกต์ ส่ง controller.signal ไปยัง fetch และเรียก controller.abort() ในการทำความสะอาด หากคอมโพเนนต์ถูกอันเมานต์ก่อนคำขอเสร็จสมบูรณ์ fetch จะถูกยกเลิกและ setState จะไม่ถูกเรียก สิ่งนี้ป้องกันเงื่อนไขการแข่งขันและข้อผิดพลาด “Can't perform a React state update on an unmounted component”

จะเกิดอะไรขึ้นถ้าฉันไม่ส่งอาร์เรย์ดีเพนเดนซี?

useEffect จะทำงานหลังจากการเรนเดอร์ทุกครั้งโดยไม่มีข้อยกเว้น ซึ่งหมายความว่า setState ใดๆ ภายในเอฟเฟกต์จะทำให้เกิดการเรนเดอร์ใหม่ → เอฟเฟกต์ใหม่ → ลูปไม่สิ้นสุด ในทางปฏิบัติ เอฟเฟกต์ที่ไม่มีอาร์เรย์ดีเพนเดนซีมักจะเป็นข้อผิดพลาดเสมอ ข้อยกเว้นคือการบันทึกหรือการซิงโครไนซ์กับระบบภายนอกที่ทุกการเรนเดอร์ต้องการการซิงโครไนซ์

สรุป

  • useEffect คือฮุกสำหรับดำเนินการเอฟเฟกต์ข้างเคียงหลังจากคอมมิต DOM แทนที่ componentDidMount, componentDidUpdate และ componentWillUnmount
  • อาร์เรย์ดีเพนเดนซี ควบคุมการทำงานซ้ำของเอฟเฟกต์; อาร์เรย์ว่าง = หนึ่งครั้ง, การละเว้นดีเพนเดนซี = stale closure
  • การทำความสะอาด จำเป็นสำหรับการสมัครรับ ตัวจับเวลา และคำขอ; หากไม่มีจะเกิดการรั่วไหลของหน่วยความจำ
  • AbortController เป็นวิธีที่ถูกต้องในการยกเลิกคำขอ fetch ภายใน useEffect ป้องกันเงื่อนไขการแข่งขัน
  • StrictMode จะเมานต์เอฟเฟกต์สองครั้งในโหมด dev เพื่อตรวจสอบความถูกต้องของการทำความสะอาด
  • แยกเอฟเฟกต์ — แต่ละ useEffect จัดการหนึ่งงาน แม้ว่าดีเพนเดนซีจะตรงกัน
  • eslint-plugin-react-hooks ตรวจสอบความสมบูรณ์ของอาร์เรย์ดีเพนเดนซีโดยอัตโนมัติ ลดข้อบกพร่อง 72%

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม