Home > Software engineering >  Infinte loop in UseEffect retrieval from Firestore
Infinte loop in UseEffect retrieval from Firestore

Time:02-10

I am trying to retrieve the document of the organization that is logged in after we see that Firebase Auth has the correct user uid. However, I'm getting an infinite loop in the useEffect function for that retrieval.

import { createContext, useEffect, useReducer} from 'react'
import { projectAuth, projectFirestore } from '../firebase'

export const AuthContext = createContext()

export const authReducer = (state, action) => {
    switch (action.type) {
        case 'LOGIN':
            return { ...state, user: action.payload }
        case 'LOGOUT':
            return { ...state, user: null }
        case 'AUTH_IS_READY':
            return { ...state, user: action.payload, authIsReady: true}
        case 'RETRIVED_ORG':
            return { ...state, org: action.payload }
        default:
            return state
    }
}

export const AuthContextProvider = ({ children }) => {
    const [state, dispatch] = useReducer(authReducer, {
        user: null,
        org: null,
        authIsReady: false
    })

    useEffect(() => {
        const unsub = projectAuth.onAuthStateChanged((user) => {
            dispatch({ type: 'AUTH_IS_READY', payload: user})
            unsub()
        })
    }, [])
    console.log('AuthContext state:', state)

    useEffect(() => {
        if (state.authIsReady) {
            projectFirestore.collection('orgs').doc(state.user.uid).get()
            .then(snapshot => {
                dispatch({ type: 'RETRIVED_ORG', payload: snapshot.data()})
            })
        }
    }, [state])

    return (
        <AuthContext.Provider value={{ ...state, dispatch }}>
            {children}
        </AuthContext.Provider>
    )
}

This is the Context for Auth. As you can see, the first useEffect is run once and when it is complete, it triggers dispatch and sets the user and 'authIsReady' state to true from the reducer.

For the second useEffect, I want to run it when the authIsReady state is true because only then, I will know I have the user.uid. This does fire and uses the dispatch and sets the org but it is running multiple times. I think this is because I am including [state] in the second parameter, but if I remove it, I get an error message: "React Hook useEffect has missing dependencies: 'state.authIsReady' and 'state.user.uid'. Either include them or remove the dependency array".

Is there a more elegant way of doing this?

CodePudding user response:

Your 2nd useEffect needs to depend just on state.authIsReady and state.user.uid. Right now its triggered in every state update, and when you do dispatch({ type: 'RETRIVED_ORG', payload: snapshot.data()}) you update the state, and it triggers 2nd useEffect again, fetches again, updates again and this is what is creating the infinite loop.

It should be:

 useEffect(() => {
        if (state.authIsReady) {
            projectFirestore.collection('orgs').doc(state.user.uid).get()
            .then(snapshot => {
                dispatch({ type: 'RETRIVED_ORG', payload: snapshot.data()})
            })
        }
    }, [state.authIsReady, state.user.uid])
  • Related