Setting state when Firebase authentication state changes in ReactJS

Viewed 318

I'm writing a ReactJS app where I have a provider class (AuthProvider.js) handle Firebase authentication. Several of its children use the auth state. Everything worked fine for about a month, but last weekend we started having problems, probably because of some upgrade and I'm trying to troubleshoot. Basically, in the AuthProvider component, I have a currentUser state that's supposed to be null when no one is logged in or contain some data about the current user when there is one. Could be just the UID. Could be the full user object. Could be a boolean value. At this point we're not picky. Basically, when I try the textbook approach of

  useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
       setCurrentUser(user);
    });

    // Cleanup subscription on unmount
    return () => unsubscribe();
  }, []);

in the AuthProvider class (with or without useEffect), I get the "Rendered fewer hooks than expected." I heard some cliche about how using setState in callback functions is a bad idea, but I have no problems when setting errors (another state object that gets exported like currentUser).

Is there a safe and reliable way for me to observe the firebase state from AuthProvider's child components, with minimal code duplication? As is common, I have several cases where I display one component to the guests and another to the members, so the state is important to me.

2 Answers

Try this:

useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
    if(user){
        setCurrentUser(user);
         }else{
        setCurrentUser(null);
      }
    });

    // Cleanup subscription on unmount
    return () => unsubscribe();
  }, []);

I think the best way is to use a global state using context-api or redux. Because user state is a very important state in your project. When you have more components, you have to pass the user state one component to other component. I think it is not a good practice and you can't to it evetytime.

If you use any state management system as I mentioned above then you can get authenticated user from onAuthStateChanged and dispatch it to global state. Then you can use that state everywhere in your project.

 useEffect(() => {

    auth.onAuthStateChanged(userAuth => {
      dispatch({
        type: actionTypes.SET_USER,
        user: userAuth,
      })
    })
  }, [])

Note : This is not the complete code first you have to create reducers and others in redux or context-api. Then you can use this.

Related