วิธีแก้ปัญหา Race Conditions ตอน fetching data ใน React
เมื่อเราลองทำ 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
อันล่าสุดที่เราต้องการแสดงผลได้ ถ้าการดึงข้อมูลเสร็จไม่พร้อมกัน
จากตัวอย่างจะเห็นว่า 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 คือ
- เมื่อแก้ไข
props.name
จะทำการ re-render และ useEffect จะทำงานอีกครั้ง - ทุกครั้งที่ re-render จะสั่ง clean-up function (return ใน useEffect) เพื่อเปลี่ยน active flag เป็น
false
- หลังจากที่ดึงข้อมูลเสร็จจะเช็คว่า active flag เป็น
false
หรือยังเพื่อไม่ให้รันsetData
ใน useEffect ที่ clean-up ไปแล้ว
เพียงแค่นี้เราก็สามารถแก้ปัญหา Race Condition ใน React ได้แล้ว แต่จริงๆแล้วการเขียน fetch data เองนั้นยังเป็นปัญหาอีกหลายอย่าง เช่น การ cache ข้อมูล ผมแนะนำว่าการใช้ data fetching จากใน framework (NextJS, Remix) จะทำให้ใช้งานได้สะดวกมากที่สุด 😆