ป้องกัน useEffect Re-Rendering อย่างเหมาะสมใน React.js

Feb 07 2023
เรื่องราวเบื้องหลัง ฉันกำลังสร้างแอปโดยใช้ Firebase ใน React และฉันรู้สึกประหลาดใจที่พบว่าฉันอ่านถึงขีดจำกัด 50,000 รายการสำหรับวันนั้นๆ แล้ว ฉันคิดไม่ออกเลยว่ามันเป็นไปได้ยังไง เพราะแอปของฉันรันในเครื่องของฉันเท่านั้น และฉันเป็นผู้ใช้คนเดียว

เรื่องราวเบื้องหลัง

ฉันกำลังสร้างแอปโดยใช้ Firebase ใน React และฉันรู้สึกประหลาดใจที่พบว่าฉันอ่านถึงขีดจำกัด 50,000 รายการสำหรับวันนั้นแล้ว ฉันคิดไม่ออกเลยว่ามันเป็นไปได้ยังไง เพราะแอปของฉันรันในเครื่องของฉันเท่านั้น และฉันเป็นผู้ใช้คนเดียว

ปัญหาที่ฉันประสบเกี่ยวข้องกับส่วนประกอบของฉันที่ประกอบขึ้นใหม่เมื่อทำการเรียกแบบอะซิงโครนัสที่เปลี่ยนตัวแปรสถานะ

นี่คือตัวอย่างวิธีที่ไม่ดีในการโทรแบบอะซิงโครนัสโดยใช้useEffect.

import {useEffect} from 'react'
function Example() {
    const [vehicles, setVehicles] = useState([])
    const vehiclesRef = collection(db, 'vehicles')

    useEffect(() => {
        const getVehicles = async () => {
            const data = await getDocs(vehiclesRef)
            setVehicles(data.docs.map( doc => ( // memory leaks 
                        {
                            ...doc.data(), 
                            id:doc.id 
                        }
                    )
                )
            )
        }

        getVehicles()
    },[vehicles])       
}

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

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

นี่คือวิธีที่ฉันแก้ไข

import {useEffect, useRef} from 'react'
function Example() {
    const [vehicles, setVehicles] = useState([])
    const vehiclesRef = collection(db, 'vehicles')

    useEffect(() => {
        let isMounted = true

        const getVehicles = async () => {
            const data = await getDocs(vehiclesRef)
            
            if(isMounted){
                setVehicles(data.docs.map( doc => (
                            {
                                ...doc.data(), 
                                id:doc.id 
                            }
                        )
                    )
                )
            }
        }

        getVehicles()

        console.log(vehicles)

        return () => { isMounted = false }
    }, [vehicles]) 
}

เมื่อisMounted คือfalseรหัสจะไม่ทำงานจนกว่าค่านั้นจะถูกตั้งค่าเป็นจริง เมื่อคอมโพเนนต์ถูกเมาต์อีกครั้ง

หวังว่านี่จะช่วยได้!

เพิ่มระดับการเข้ารหัส

ขอบคุณที่เป็นส่วนหนึ่งของชุมชนของเรา! ก่อนที่คุณจะไป:

  • ปรบมือให้กับเรื่องราวและติดตามผู้เขียน
  • ดูเนื้อหาเพิ่มเติมในสิ่งพิมพ์ Level Up Coding
  • ติดตามเรา: Twitter | LinkedIn | จดหมายข่าว