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
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.
and then clean up the effect. In my case exact same code works.
Try this:
Update:
You should import User interface from "firebase/auth" not "@firebase/auth-types";