React.js で useEffect の再レンダリングを適切に防止する

Feb 07 2023
バックストーリー React で Firebase を使用してアプリを構築していましたが、その日の読み取り制限 50,000 に達したことに気づき、驚きました。私のアプリは私のマシン上でローカルにのみ実行され、私は唯一のユーザーだったので、いったいどのようにしてそれが可能になるのか理解できませんでした。

バックストーリー

React で Firebase を使用してアプリを構築していましたが、その日の読み取り制限 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 に設定されるまで、コードは実行されません。

お役に立てれば!

レベルアップコーディング

コミュニティにご参加いただきありがとうございます。出発する前に:

  • ストーリーに拍手を送り、著者をフォローしてください
  • 『レベルアップコーディング』出版物でさらにコンテンツを表示
  • フォローしてください: Twitter | リンクトイン| ニュースレター