My React context is not updating and seems to be one step behind the currentstate

Viewed 49

I am building a simple authentication page with React using firebase. I have a context that wraps that sign in page. I am updating the context in case an error occurs during the signinWithPopup function (I am using the facebook provider). This context change is not happening immediately, but have to wait for the next render for it to occur. I ran through the code with a debuger and the code setIsAuthError (true) is running, but the isAuthError variable is not being updated.

contextfile.js

import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase.js";
import {
   createUserWithEmailAndPassword,
   signInWithEmailAndPassword,
   fetchSignInMethodsForEmail, SignInMethod,
   FacebookAuthProvider,
   onAuthStateChanged, signOut,
   GoogleAuthProvider, signInWithPopup,
} from "firebase/auth";

const AuthContext = React.createContext();

export const authErrorProcessCodes = {
   "LOGIN": 1,
   "SIGNUP": 2,
   "LOGOUT": 5,
   "GOOGLE": 3,
   "FB": 4,
   "TWITTER": 6,
   "FETCH_LOGIN_METHOD": 7,
   "SWTICH_USER": 8
};


export function useAuth() {
   return useContext(AuthContext);
}

export function AuthProvider({ children }) {

   const [currentUser, setCurrentUser] = useState(null);
   const [isAuthError, setIsAuthError] = useState(false);
   const [authErrorObject, setAuthErrorObject] = useState({});

   const value = {
      currentUser,
      signup, signout, login,
      loginWithGoogleAccount,
      loginWithFacebookAccount,
      isAuthError,
      authErrorObject,
   };

   function setErrorObject(msg, code, email) {
      setAuthErrorObject({
         "message": msg,
         "code": code,
         "email": email,
      });
   }

   useEffect(() => {
      const unsubscribe = onAuthStateChanged(auth, (user) => {
         console.log("(In Context) onAuthStateChanged(): User.user=" + JSON.stringify(user));
         if (user) {
            setCurrentUser(user);
         }
         else {
            setCurrentUser(null);
         }
      });
      return unsubscribe;
   }, [currentUser]);

   async function loginWithFacebookAccount() {
      //  console.log("(loginWithFacebookAccount()) Signing in with facebook")
      const provider = new FacebookAuthProvider();
      provider.setCustomParameters({
         prompt: "select_account"
       });

      setIsAuthError(false);
      setErrorObject("", 0, "");
      
      try {
         var res = await signInWithPopup(auth, provider);
         //    console.log("loginWithFacebookAccount() : Login sucess...<" + re.user + ">");
         setCurrentUser(res.user);
         setIsAuthError(false);
         setErrorObject("", 0, "");
      } catch (err) {
         var x = isAuthError;
          console.log("loginWithFacebookAccount (): Login Failure" + err.message);
         if (err.code == 'auth/account-exists-with-different-credential') {
            console.log("credential conflict found. setting AuthError=true");
            setIsAuthError (true); // THIS IS NOT WORKING....

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

SignUp.js

    import { Card, Form, Button, Container, Alert } from "react-bootstrap";
    import React, { useEffect, useRef, useState } from "react";
    import { useAuth } from "../../Contexts/AuthContext";
    
    const SignUp = () => {
       const emailRef = useRef();
       const passwordRef = useRef();
       const passwordConfirmRef = useRef();
       const {  loginWithFacebookAccount, loginWithGoogleAccount, login, signup,  
          isAuthError,  authErrorObject, currentUser, signout } = useAuth();
       const [error, setError] = useState("");
       const [loading, setLoading] = useState(false);
       const [isError, setIsError] = useState(false);
    
      async function handleLoginWithFB(e) {
          try {
             setError("");
             setIsError(false);
             setLoading(true);
             console.log(emailRef.current.value);
             var res =  await loginWithFacebookAccount();
             console.log("(handleLoginWithFB()- after async function call "  + isAuthError );
             if (isAuthError) {
                setIsError(true);
                setError(authErrorObject.message);
                console.log(" **** (SignUp) IsAuthError is true: <" +  authErrorObject.message + ">****");
             }
           
          } catch (err) {
             console.log(err);
             setIsError(true);
             setLoading(false);
          }
       }
    
    
       useEffect(() => { // this hook will get called everytime when myArr has changed
          // perform some action which will get fired everytime when myArr gets updated
             console.log('Updated State', isAuthError)
          }, [isAuthError, authErrorObject, isError]);


   return (
      <Container sm
         className="d-flex justify-content-center"
         style={{ minHeight: "50vh" }}>
         <Card style={{ width: "25vw" }}>
            <Card.Body>
               <h2>Sign Up</h2>
               {isAuthError && <Alert variant="danger">{error}</Alert>}
               <Form onSubmit={handleSubmit}>
                  <Form.Group>
                     <Form.Label>Email</Form.Label>
                     <Form.Control
                        type="email"
                        ref={emailRef}
                        required
                     ></Form.Control>
                  </Form.Group>
                  <Form.Group>
                     <Form.Label>Password</Form.Label>
                     <Form.Control
                        type="password"
                        ref={passwordRef}
                        required
                     ></Form.Control>
                  </Form.Group>
                  <Form.Group>
                     <Form.Label>Password Confirm</Form.Label>
                     <Form.Control
                        type="password"
                        ref={passwordConfirmRef}
                        required
                     ></Form.Control>
                  </Form.Group>
                  <div style={{ textAlign: "center" }}>
                     <Button type="submit" width="100%">
                        Signup
                     </Button>
                     <Button type="button" width="100%" onClick={handleLogout}>
                        Logout
                     </Button>
                     <Button type="button" width="100%" onClick={handleLogin}>
                        Login
                     </Button>
                  </div>
                  <div width="100%" style={{ textAlign: "center" }}>
                  <Button type="button" width="100%" onClick={handleLoginWithGoogle}>
                        Login With Google Account
                     </Button>
                  </div>
                  <div width="100%" style={{ textAlign: "center" }}>
                  <Button type="button" width="100%" onClick={handleLoginWithFB}>
                        Login With Facebook Account
                  </Button>
                  </div>
                  <div>You are currently loged in as: {currentUser ? currentUser.email : "No User"}</div>
               </Form>
            </Card.Body>
         </Card>

      </Container>
   );
};

export default SignUp;

output to console is as follows:

Updated State false
AuthContext.js:171 loginWithFacebookAccount (): Login FailureFirebase: Error (auth/account-exists-with-different-credential).
AuthContext.js:174 credential conflict found. setting AuthError=true
AuthContext.js:124 getting sign in method for: shareef.abdelhaleem@gmail.com
SignUp.js:107 (handleLoginWithFB()- after async function call false
SignUp.js:124 Updated State true
AuthContext.js:126 ['google.com']
AuthContext.js:130 gmail FOUND...
SignUp.js:124 Updated State true

you will notice that SignUp.js:107 (handleLoginWithFB()- after async function call false . this should be SignUp.js:107 (handleLoginWithFB()- after async function call true becuase i chnaged it in the context

Now, I have been told that changing the state will not change the state immediatly and that changes will be reflected on the next render, however, state changes being picked up immediatly when not set in a catch block. !!! this is very strange to me. Why the differance? also, would it help if i used a useReduer to change the state...???

Thanks.

0 Answers
Related