Аргумент типа «Пользователь | null» нельзя присвоить параметру типа «SetStateAction<User | ноль>'

Пытаемся реализовать авторизацию пользователя через Firebase в новом проекте React.

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

  // ...

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

  const auth = getAuth();

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

Ошибка на setUser(newUser); :

Аргумент типа «Пользователь | null» нельзя присвоить параметру типа >'SetStateAction<User | ноль>'. Тип «Пользователь» не может быть назначен типу «SetStateAction<User | ноль>'. В типе «Пользователь» отсутствуют следующие свойства типа «Пользователь»: linkAndRetrieveDataWithCredential, linkWithCredential, linkWithPhoneNumber, linkWithPopup и еще 14.ts(2345)

Пробовал делать newUser: User, что не исправило эту ошибку. useState<any | null> решает эту проблему, но я считаю, что это противоречит цели Typescript.

newUser: React.SetStateAction<User | null> приводит к другой ошибке:

Аргумент типа «(newUser: React.SetStateAction<User | null>) => void» не может быть назначен параметру типа «NextOrObserver». Тип «(newUser: React.SetStateAction<User | null>) => void» не может быть назначен типу «NextFn<User | ноль>'. Типы параметров newUser и value несовместимы. Введите «Пользователь | null» нельзя присвоить типу «SetStateAction<User | ноль>'. Тип «Пользователь» не может быть назначен типу «SetStateAction<User | ноль>'. В типе «Пользователь» отсутствуют следующие свойства типа «Пользователь»: linkAndRetrieveDataWithCredential, linkWithCredential, linkWithPhoneNumber, linkWithPopup и еще 14.ts(2345)

Я считаю, что это просто предупреждения, так как все по-прежнему работает правильно, но я хотел бы решить эту проблему, несмотря ни на что. Не уверен, что еще попробовать, так как я очень новичок в Typescript.

Весь этот файл:

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.info(error.code, error.message);
      });
    } else {
      signInWithEmailAndPassword(auth, email, password)
        .catch((error: { code: any; message: any; }) => {
          console.info(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;

🤔 А знаете ли вы, что...
React - это библиотека JavaScript, разработанная компанией Facebook.


1
536
2

Ответы:

Первое, что приходит на ум, это то, что этот побочный эффект должен быть реализован внутри useEffect, он должен срабатывать при первом монтировании компонента.

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

а затем очистить эффект. В моем случае работает точно такой же код.


Решено

Попробуй это:

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

Обновлять:

Вы должны импортировать пользовательский интерфейс из «firebase/auth», а не «@firebase/auth-types»;

import { User } from "firebase/auth";