skip to Main Content

Trying to implement user authorization through Firebase in a new React project.

  import { User } from '@firebase/auth-types';

  // ...

  const [user, setUser] = useState<User | null>(null);

  const auth = getAuth();

  onAuthStateChanged(auth, (newUser) => {
    setUser(newUser);
  });

Error on setUser(newUser); :

Argument of type ‘User | null’ is not assignable to parameter of type >’SetStateAction<User | null>’.
Type ‘User’ is not assignable to type ‘SetStateAction<User | null>’.
Type ‘User’ is missing the following properties from type ‘User’: linkAndRetrieveDataWithCredential, linkWithCredential, linkWithPhoneNumber, linkWithPopup, and 14 more.ts(2345)

Tried doing newUser: User which did not fix this error. useState<any | null> resolves it, but I believe this defeats the purpose of Typescript.

newUser: React.SetStateAction<User | null> results in another error:

Argument of type ‘(newUser: React.SetStateAction<User | null>) => void’ is not assignable to parameter of type ‘NextOrObserver’.
Type ‘(newUser: React.SetStateAction<User | null>) => void’ is not assignable to type ‘NextFn<User | null>’.
Types of parameters ‘newUser’ and ‘value’ are incompatible.
Type ‘User | null’ is not assignable to type ‘SetStateAction<User | null>’.
Type ‘User’ is not assignable to type ‘SetStateAction<User | null>’.
Type ‘User’ is missing the following properties from type ‘User’: linkAndRetrieveDataWithCredential, linkWithCredential, linkWithPhoneNumber, linkWithPopup, and 14 more.ts(2345)

I believe these are just warnings, since everything still works properly, but I would like to resolve this regardless. Not sure what else to try as I’m very new to Typescript.

Entirety of this file:

import React, { useState, useEffect } from 'react';
import { getAuth, onAuthStateChanged, createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth';
import { User } from '@firebase/auth-types';

function EmailPasswordForm(): JSX.Element {
  const [isCreatingAccount, setIsCreatingAccount] = useState(false);
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');

  const auth = getAuth();

  const onButtonClick = () => {
    if (isCreatingAccount) {
      createUserWithEmailAndPassword(auth, email, password)
      .catch((error) => {
        console.log(error.code, error.message);
      });
    } else {
      signInWithEmailAndPassword(auth, email, password)
        .catch((error: { code: any; message: any; }) => {
          console.log(error.code, error.message);
        });
    }
  }

  const onEmailChange = (e: { target: { value: React.SetStateAction<string>; }; }) => setEmail(e.target.value);
  const onPasswordChange = (e: { target: { value: React.SetStateAction<string>; }; }) => setPassword(e.target.value);
  const onConfirmPasswordChange = (e: { target: { value: React.SetStateAction<string>; }; }) => setConfirmPassword(e.target.value);

  const createAccountForm = (
    <>
      <input placeholder="e-mail" onChange={onEmailChange} />
      <input placeholder="password" type="password" onChange={onPasswordChange} />
      <input placeholder="confirm password" type="password" onChange={onConfirmPasswordChange} />
    </>
  );

  const signInForm = (
    <>
      <input placeholder="e-mail" onChange={onEmailChange} />
      <input placeholder="password" type="password" onChange={onPasswordChange} />
    </>
  );

  return (
    <>
      {isCreatingAccount ? createAccountForm : signInForm}
      <button type="button" onClick={onButtonClick}>{isCreatingAccount ? 'create account' : 'sign in'}</button>
      <button className="text-button" type="button" onClick={() => setIsCreatingAccount(!isCreatingAccount)}>
        {isCreatingAccount ? 'i don't have an account!' : 'i already have an account!'}
      </button>
    </>
  );
}

function SignIn(): JSX.Element {
  const [user, setUser] = useState<User | null>(null);

  const auth = getAuth();

  onAuthStateChanged(auth, (newUser) => {
    setUser(newUser);
  });

  if (user != null) {
    return <span>you are signed in!</span>;
  }

  return (
    <div className="center">
      <EmailPasswordForm />
    </div>
  );
}

export default SignIn;

2

Answers


  1. The first thing that comes to the mind, is that this side effect should be done inside of useEffect, it should trigger when the component first mounts.

    useEffect(() =>{
           const unlisten = onAuthStateChanged(
              authUser => {
                authUser
                  ? setAuthUser(authUser)
                  : setAuthUser(null);
              },
           );
           return () => {
               unlisten();
           }
     }, []);
    

    and then clean up the effect. In my case exact same code works.

    Login or Signup to reply.
  2. Try this:

    const [user, setUser] = useState<User | null>(null);
    
    useEffect(() => {
      const auth = getAuth();
      const unsubscribe = onAuthStateChanged(auth, user => {
        if (user) {
          setUser(user);
        }
      });
      // Don't listen on stateChange anymore if component did unmount.
      return () => {
       unsubscribe();
      }
    }, []);
    

    Update:

    You should import User interface from "firebase/auth" not "@firebase/auth-types";

    import { User } from "firebase/auth";
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search