วิธีแก้ปัญหา Race Conditions ตอน fetching data ใน React

Komkanit
codesxdiary
Published in
2 min readApr 27, 2023

เมื่อเราลองทำ fetching dataใน React โดยทั่วไปแล้วเราจะทำการ fetch โดยใช้ useEffect เพื่อให้สามารถอัพเดตข้อมูลได้ตลอดเวลาที่มีการเปลี่ยนค่า input
เราจึงลองสร้าง function useEffect ขึ้นมา และให้ทำการ fetch ข้อมูลใหม่ทุกครั้งที่มีการเปลี่ยนแปลงของ props.name และอัพเดตการแสดงผลโดย setData(newData) โค้ดจะออกมาดูเรียบง่ายเหมือนในตัวอย่างข้างล่างนี้

useEffect(() => {
const fetchData = async () => {
const response = await fetch(`https://example.com/search?name=${props.name}`);
const newData = await response.json();
setData(newData); // update data
};

fetchData(); // fetchData run when changing props.name

}, [props.name]);

แต่เมื่อมองดีๆจะเห็นว่าในโค้ดชุดนี้มีช่องโหว่อยู่ เพื่อนๆลองคิดคำตอบไว้ในใจ…

ใช่แล้ว ถ้า props.name นั้นถูกเปลี่ยนไปเรื่อยๆในระหว่างที่ดึงข้อมูล จะทำให้ข้อมูลที่ถูกแสดงผลโดยการ setData นั้นอาจจะไม่ใช่ข้อมูลที่เกิดจาก props.name อันล่าสุดที่เราต้องการแสดงผลได้ ถ้าการดึงข้อมูลเสร็จไม่พร้อมกัน

Race condition problem in fetching data

จากตัวอย่างจะเห็นว่า props name=John นั้นถูกส่งไปก่อน name=Nick แต่ดึงข้อมูลเสร็จช้ากว่า ทำให้ผลลัพธ์สุดท้ายจะทำการแสดงผล John แทนที่จะเป็น Nick ที่เป็น input ล่าสุดที่เราส่งไป นี่คือปัญหา Race Conditions นี่เอง

เราสามารถแก้ปัญหานี้ได้ง่ายๆโดยการเพิ่ม boolean flag เข้าไปใน useEffect

useEffect(() => {
let active = true; // boolean flag to prevent race condition

const fetchData = async () => {
const response = await fetch(`https://example.com/search?name=${props.name}`);
const newData = await response.json();

if (active) { // check active flag before set data
setData(newData); // update data
}
};

fetchData();

return () => {
active = false; // set active to false in clean-up function
};
}, [props.name]);

ขั้นตอนการทำงานของ useEffect คือ

  1. เมื่อแก้ไข props.name จะทำการ re-render และ useEffect จะทำงานอีกครั้ง
  2. ทุกครั้งที่ re-render จะสั่ง clean-up function (return ใน useEffect) เพื่อเปลี่ยน active flag เป็น false
  3. หลังจากที่ดึงข้อมูลเสร็จจะเช็คว่า active flag เป็น false หรือยังเพื่อไม่ให้รัน setData ใน useEffect ที่ clean-up ไปแล้ว

เพียงแค่นี้เราก็สามารถแก้ปัญหา Race Condition ใน React ได้แล้ว แต่จริงๆแล้วการเขียน fetch data เองนั้นยังเป็นปัญหาอีกหลายอย่าง เช่น การ cache ข้อมูล ผมแนะนำว่าการใช้ data fetching จากใน framework (NextJS, Remix) จะทำให้ใช้งานได้สะดวกมากที่สุด 😆

--

--