Home > Enterprise >  Argument of type 'User | null' is not assignable to parameter of type 'SetStateAction
Argument of type 'User | null' is not assignable to parameter of type 'SetStateAction

Time:11-28

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. Not sure what else to try as I'm very new to Typescript.

Entirety of this file:

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

function EmailPasswordForm(): JSX.Element {
  const [isCreatingAccount, setIsCreatingAccount] = useState(false);

  const createAccountForm = (
    <>
      <input placeholder="e-mail" />
      <input placeholder="password" type="password" />
      <input placeholder="confirm password" type="password" />
    </>
  );

  const signInForm = (
    <>
      <input placeholder="e-mail" />
      <input placeholder="password" type="password" />
    </>
  );

  return (
    <>
      {isCreatingAccount ? createAccountForm : signInForm}
      <button type="button">{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;

CodePudding user response:

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.

CodePudding user response:

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();
  }
}, []);
  • Related