【发布时间】:2021-09-28 13:23:13
【问题描述】:
在我的 React 应用程序中,我配置了使用社交媒体和电子邮件和密码的登录。登录有效,但我重定向到主页失败。在控制台中,我收到以下错误:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
LoginPage@http://localhost:3000/static/js/main.chunk.js:3184:101
Route@http://localhost:3000/static/js/vendors~main.chunk.js:109151:29
我有一个名为AuthProvder 的上下文,其中包含以下内容:
import React, { Component, createContext, useContext, useState, useEffect } from "react";
import firebase from "firebase/app";
import { auth, generateUserDocument, projectFirestore, Providers } from "../config/firebase";
import ILocalLoginData from "../interfaces/locallogindata.interface";
import { IProfile } from "../interfaces/profile.interface";
// const UserContext = createContext<Partial<ContextProps>>({});
const AuthContext = createContext({} as any);
const useAuth = () => {
return useContext(AuthContext);
}
const AuthProvider = ({children}: any) => {
const [currentUser, setCurrentUser] = useState<any>(null);
const [loading, setLoading] = useState(true);
// create the user account in the app database
// this only needs to be done for the password provider
// because social media login will create this information
// for the app
const signup = async (
provider: firebase.auth.AuthProvider,
values?: IProfile
) => {
if (provider.providerId === "password")
{
const creds = auth.createUserWithEmailAndPassword(values?.email as string, values?.password as string);
// add a document to the user collection
await projectFirestore.collection("users").add({...values});
return creds;
}
return
}
const login = (
provider: firebase.auth.AuthProvider,
values?: ILocalLoginData
) => {
// perform the correct login based on the provider
if (provider.providerId === "password")
{
return auth.signInWithEmailAndPassword(values?.email as string, values?.password as string);
} else {
return auth.signInWithPopup(provider);
}
}
const logout = () => {
return auth.signOut();
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async user => {
const usr = await generateUserDocument(user);
setCurrentUser(usr);
setLoading(false);
})
return unsubscribe;
}, []);
const value = {
currentUser,
login,
logout,
signup
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
export { AuthProvider, AuthContext, useAuth };
可以看出,我正在从 useEffect 函数返回一个 unsubscribe 函数。
LoginPage.tsx如下:
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import firebase from "firebase/app";
import IPageProps from "../../interfaces/page.interface";
import { SignIn } from "../../modules/auth";
import { Providers } from "../../config/firebase";
import SiteNavbar from "../../comps/Navbar";
import ILocalLoginData from "../../interfaces/locallogindata.interface";
import { useAuth } from "../../contexts/AuthProvider";
const LoginPage: React.FC<IPageProps> = props => {
const [authenticating, setAuthenticating] = useState<boolean>(false);
const [error, setError] = useState<string>('');
const [loading, setLoading] = useState<boolean>(false);
const [values, setValues] = useState<ILocalLoginData>({
email: "",
password: ""
});
const { login } = useAuth();
const history = useHistory();
const handleLogin = async (e: any, provider: firebase.auth.AuthProvider) => {
e.preventDefault();
try {
setError("");
setLoading(true);
await login(provider, values);
history.push("/");
} catch {
setError("Failed to log in");
}
setLoading(false);
}
// Handle the changes made in the login form so that the values
// can be extracted
const handleChange = (e: any) => {
e.persist();
setValues(values => ({
...values,
[e.target.name]: e.target.value
}));
}
return (
<div>
<SiteNavbar />
<div className="flex h-screen bg-yellow-700">
<div className="max-w-xs w-full m-auto bg-yellow-100 rounded p-5">
<header>
<img alt="" className="w-20 mx-auto mb-5" src="https://img.icons8.com/fluent/96/000000/tiger.png" />
</header>
<form>
<div>
<label className="block mb-2 text-yellow-500" htmlFor="email">Email</label>
<input className="w-full p-2 mb-6 text-yellow-700 border-b-2 border-yellow-500 outline-none focus:bg-gray-300"
type="text"
name="email"
value={values.email}
placeholder="Enter your email address"
onChange={handleChange} />
</div>
<div>
<label className="block mb-2 text-yellow-500" htmlFor="password">Password</label>
<input className="w-full p-2 mb-6 text-yellow-700 border-b-2 border-yellow-500 outline-none focus:bg-gray-300"
type="password"
name="password"
value={values.password}
onChange={handleChange} />
</div>
<div>
<button className="w-full bg-yellow-700 hover:bg-pink-700 text-white font-bold py-2 px-4 mb-6 rounded"
onClick={(e) => handleLogin(e, Providers.email)}>
Login
</button>
</div>
</form>
<div>
<button
className="w-full bg-gray-100 hover:bg-pink-700 text-black font-bold py-2 px-4 mb-6 rounded"
disabled={authenticating}
onClick={(e) => handleLogin(e, Providers.google)}
>
<i className="fa fa-google"></i> Login in with Google
</button>
<button
className="w-full bg-indigo-700 hover:bg-pink-700 text-white font-bold py-2 px-4 mb-6 rounded"
disabled={authenticating}
onClick={(e) => handleLogin(e, Providers.facebook)}
>
<i className="fa fa-facebook-square"></i> Login in with Facebook
</button>
</div>
</div>
</div>
</div>
)
}
export default LoginPage;
获取所有用户详细信息的函数是:
import firebase from 'firebase/app';
import 'firebase/storage';
import 'firebase/firestore';
import 'firebase/auth';
import config from './config';
const Firebase = firebase.initializeApp(config.firebase);
const auth = firebase.auth();
const projectStorage = Firebase.storage();
const projectFirestore = Firebase.firestore();
const timestamp = firebase.firestore.FieldValue.serverTimestamp;
const Providers = {
google: new firebase.auth.GoogleAuthProvider(),
email: new firebase.auth.EmailAuthProvider(),
facebook: new firebase.auth.FacebookAuthProvider(),
}
// Create function that will add any new uses to the user table
const generateUserDocument = async (user: any, additionalData: any = null) => {
// return if no user has been set
if (!user) return;
const userRef = projectFirestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();
// if a snapshot does not exist, e.g. the user does not exist
// add them to the the collection
if (!snapshot.exists) {
const { email, displayName, photoURL } = user;
try {
await userRef.set({
displayName,
email,
photoURL,
enabled: true,
permitted: false,
...additionalData,
});
} catch (error) {
console.error("Error creating user document", error);
}
}
return getUserDocument(user.uid);
};
// get information about the user
const getUserDocument = async (uid: string) => {
if (!uid) return null;
try {
const userDocument = await projectFirestore.doc(`users/${uid}`).get();
// if the user is not permitted or not enabled return null
if (userDocument.exists) {
const doc = userDocument.data();
if (!doc?.permitted) {
console.log("not permitted");
return null;
} else if (!doc?.enabled) {
console.log("not enabled");
return null;
} else {
console.log("happy days");
return {
uid,
...userDocument.data(),
};
}
}
} catch (error) {
console.error("Error fetching user", error);
}
};
export { projectStorage, projectFirestore, timestamp, auth, Providers, generateUserDocument};
控制台日志只是为了让我了解正在发生的事情。事实上,我得到了“快乐的日子”输出,但是在上面的错误之后。这让我感到困惑,因为它发生在 useEffect 块中的 onAuthStateChanged 函数中,为什么它没有安装?
我假设这是我缺少的一些简单的东西。该应用程序可以工作并且该人已登录,但令人讨厌的是重定向到主页不起作用。手动导航到主页。
【问题讨论】:
标签: reactjs typescript firebase