React Memoization: เมื่อ useMemo และ React.memo พลิกโฉม
เจาะลึกปัญหาการทำ Memoization ใน React ที่อาจทำให้คอมโพเนนต์เรนเดอร์บ่อยเกินคาด พร้อมวิธีแก้ไขปัญหา reference ไม่เสถียร

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- การใช้ React.memo และ useMemo อาจล้มเหลวหาก reference ของข้อมูลเปลี่ยนแปลงทุกครั้ง
- React.memo ใช้การเปรียบเทียบแบบ shallow comparison ผ่าน Object.is เป็นค่าเริ่มต้น
- การกำหนด dependency array ไม่ครบถ้วนใน useMemo ส่งผลให้เกิดปัญหา stale closures
- การทำ Memoization เพิ่มความซับซ้อนและ overhead ควรใช้เมื่อจำเป็นจริงๆ เท่านั้น
การใช้งานเครื่องมือเพิ่มประสิทธิภาพใน React อย่าง React.memo หรือ useMemo มักมาพร้อมความคาดหวังว่าแอปพลิเคชันจะทำงานเร็วขึ้น ลดจำนวนการเรนเดอร์ และได้เฟรมเรตที่ลื่นไหล ทว่าในความเป็นจริง นักพัฒนาหลายคนมักพบปัญหาว่าคอมโพเนนต์ยังคงเรนเดอร์บ่อยกว่าที่ควรจะเป็น หรือแย่กว่านั้นคือแสดงข้อมูลเก่าที่ไม่ยอมอัปเดตตามสถานะปัจจุบัน
สาเหตุหลักเบื้องหลังปัญหาเหล่านี้ไม่ได้มาจากความผิดพลาดของ React แต่เกิดจากกลไกการตรวจสอบความเท่ากัน (equality check) และการติดตาม dependency ที่มีความซับซ้อน โดย React.memo เป็น Higher-Order Component ที่ใช้การเปรียบเทียบแบบ shallow comparison ของ props ด้วย Object.is หากมีการสร้างออบเจกต์ขึ้นมาใหม่ทุกครั้งที่คอมโพเนนต์แม่เรนเดอร์ React.memo จะเข้าใจทันทีว่า props มีการเปลี่ยนแปลง แม้ว่าภายในออบเจกต์จะมีค่าเท่าเดิมก็ตาม
การทำความเข้าใจเรื่อง Reference Equality ใน JavaScript เป็นหัวใจสำคัญของการใช้งาน React Memoization เนื่องจากออบเจกต์และอาร์เรย์ใน JavaScript ถูกเทียบความเท่ากันด้วยตำแหน่งในหน่วยความจำ (Reference) ไม่ใช่โครงสร้างภายใน การสร้างออบเจกต์หรือฟังก์ชันขึ้นใหม่ในทุกๆ เรนเดอร์ลูปจึงทำลายความเสถียรที่ระบบ Memoization ต้องการ

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
ในฝั่งของ useMemo ซึ่งทำหน้าที่แคชค่าที่คำนวณไว้ระหว่างการเรนเดอร์ ก็เผชิญกับความท้าทายที่คล้ายคลึงกัน หากฟังก์ชันภายในมีการคำนวณราคาแพงแต่ dependency ที่ระบุไว้เป็นออบเจกต์หรืออาร์เรย์ที่ถูกสร้างใหม่ทุกเรนเดอร์ การแคชนั้นจะใช้งานไม่ได้ผลทันที และค่าจะถูกคำนวณใหม่ทุกรอบ นอกจากนี้ ปัญหาเรื่อง stale closures หรือการลืมใส่ตัวแปรลงใน dependency array ก็เป็นอีกหนึ่งจุดบอดที่ทำให้ UI ไม่ยอมอัปเดตเมื่อข้อมูลเปลี่ยนแปลง
แนวทางในการแก้ปัญหาและตรวจสอบข้อผิดพลาดเหล่านี้มีด้วยกันหลายวิธี นักพัฒนาสามารถใส่ console.log เพื่อตรวจสอบค่า props และใช้ Object.is ในการเช็คว่า reference มีการเปลี่ยนแปลงหรือไม่ รวมถึงการทดลองถอด React.memo หรือ useMemo ออกชั่วคราวเพื่อสังเกตพฤติกรรมที่เปลี่ยนไป นอกจากนี้ การเขียน custom comparator สำหรับ React.memo เป็นอีกทางเลือกหนึ่ง แต่ต้องระวังเรื่องบักที่อาจเกิดขึ้นหากเขียนเงื่อนไขไม่รัดกุมพอ
ท้ายที่สุดแล้ว การทำ Memoization ไม่ใช่เรื่องฟรีแต่มันมาพร้อมกับความซับซ้อนและต้นทุนในการประมวลผล หากคอมโพเนนต์ของคุณมีราคาในการเรนเดอร์ถูก หรือแอปพลิเคชันไม่ได้ประสบปัญหาคอขวดด้านประสิทธิภาพ การใส่ Memoization ตั้งแต่ต้นอาจเป็นการปรับแต่งที่เร็วเกินไป (Premature Optimization) การถือว่า Memoization เป็นสัญญาข้อตกลงระหว่างนักพัฒนากับ React จึงเป็นสิ่งสำคัญในการรักษาเสถียรภาพของโค้ด
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น