Правильно предотвратите повторный рендеринг useEffect в 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, код не будет выполняться до тех пор, пока это значение не будет установлено в true, когда компонент снова будет смонтирован.

Надеюсь это поможет!

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • Хлопайте за историю и подписывайтесь на автора
  • Смотрите больше контента в публикации Level Up Coding
  • Подписывайтесь на нас: Твиттер | Линкедин | Новостная рассылка