user1088793
user1088793

Reputation: 653

How to prevent infinite loop caused by onAuthStateChanged Firebase Auth

I have a provider component that sets the initial auth context from firebase-auth.

Everything works fine until I try to add persistence in the form of setting up an observer with onAuthStateChanged. This checks for auth and I update my state via dispatch method.

But this is causing an infinite loop. I added an unsubscribe function call, but this makes no difference,

Can anyone advise? thanks

AuthContext.js

import React from "react";
import * as firebase from "firebase/app";
//firebaseauth reducer
import { firebaseAuth } from "../reducers/AuthReducer";

export const Auth = React.createContext();

const initialState = { user: {} };


export const AuthProvider = (props) => {
  const [state, dispatch] = React.useReducer(firebaseAuth, initialState);
  const value = { state, dispatch };

  const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
    dispatch({
      type: "HYDRATE_AUTH",
      payload: user,
    });
  });
  unsubscribe();

  return <Auth.Provider value={value}>{props.children}</Auth.Provider>;
};

Upvotes: 6

Views: 2717

Answers (2)

micahg
micahg

Reputation: 39

Thanks to @tomek-ch -- an alternate solution would be to prevent the re-render by storing the state (eg: keep the user bits in the component state) and then do not update the state if its already there (or the same).

In my case I just keep the user the first time, set a boolean state flag, and ignore subsequent events.

Upvotes: 0

tomek-ch
tomek-ch

Reputation: 157

What is happening is:

  1. The component renders
  2. It sets the auth listener
  3. The listener fires and sets the state of the component
  4. State update causes the component to rerender
  5. The component adds another listener and everything repeats

Unsubscribing doesn't help because the component keeps rerendering and adding a new listener every time.

We can tell the component to set the listener only once by using useEffect():

AuthContext.js

import React from "react";
import * as firebase from "firebase/app";
//firebaseauth reducer
import { firebaseAuth } from "../reducers/AuthReducer";

export const Auth = React.createContext();

const initialState = { user: {} };


export const AuthProvider = (props) => {
  const [state, dispatch] = React.useReducer(firebaseAuth, initialState);
  const value = { state, dispatch };

  React.useEffect(() => {
    firebase.auth().onAuthStateChanged((user) => {
      dispatch({
        type: "HYDRATE_AUTH",
        payload: user,
      });
    });
  }, []);
   

  return <Auth.Provider value={value}>{props.children}</Auth.Provider>;
};

By providing an empty dependency array to useEffect(), we tell it to run the callback only once, when the component initially renders, so the auth listener is set only once.

Upvotes: 4

Related Questions