英文:
How to update state from firebase auth in my react application
问题
你可以成功使用下面的代码进行登录,但是我认为 React 并没有从谷歌那里收到任何授权数据来更新 React 中的当前用户信息。
我知道我在这里漏掉了一些东西,但我不知道是什么。任何帮助将不胜感激!
/* eslint-disable jsx-a11y/anchor-is-valid */
import { useState } from 'react';
import * as Yup from 'yup';
import clsx from 'clsx';
import { Link } from 'react-router-dom';
import { useFormik } from 'formik';
import { getUserByToken, login } from '../core/_requests';
import { toAbsoluteUrl } from '../../../../_metronic/helpers';
import { useAuth } from '../core/Auth';
import {
auth,
db,
signInWithGoogle,
logInWithEmailAndPassword,
registerWithEmailAndPassword,
sendPasswordReset,
logout,
} from "../../../../firebase.js";
import { Firestore } from 'firebase/firestore';
import { useNavigate } from 'react-router-dom';
const loginSchema = Yup.object().shape({
email: Yup.string()
.email('Wrong email format')
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Email is required'),
password: Yup.string()
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Password is required'),
});
const initialValues = {
email: 'admin@demo.com',
password: 'demo',
};
export function Login() {
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const formik = useFormik({
initialValues,
validationSchema: loginSchema,
onSubmit: async (values, { setStatus, setSubmitting }) => {
setLoading(true);
try {
await logInWithEmailAndPassword(values.email, values.password);
setLoading(false);
navigate('/dashboard');
} catch (error) {
console.error(error);
setSubmitting(false);
setLoading(false);
//setStatus(error.message);
}
},
});
return (
<form
className='form w-100'
onSubmit={formik.handleSubmit}
noValidate
id='kt_login_signin_form'
>
{/* begin::Heading */}
<div className='text-center mb-10'>
<h1 className='text-dark mb-3'>Sign In to Web Construct</h1>
<div className='text-gray-400 fw-bold fs-4'>
New Here?{' '}
<Link to='/auth/registration' className='link-primary fw-bolder'>
Create an Account
</Link>
</div>
</div>
{/* begin::Heading */
{formik.status ? (
<div className='mb-lg-15 alert alert-danger'>
<div className='alert-text font-weight-bold'>{formik.status}</div>
</div>
) : (
<div className='mb-10 bg-light-info p-8 rounded'>
<div className='text-info'>
Use account <strong>admin@demo.com</strong> and password{' '}
<strong>demo</strong> to continue.
</div>
</div>
)}
{/* begin::Form group */}
<div className='fv-row mb-10'>
<label className='form-label fs-6 fw-bolder text-dark'>Email</label>
<input
placeholder='Email'
{...formik.getFieldProps('email')}
className={clsx(
'form-control form-control-lg form-control-solid',
{ 'is-invalid': formik.touched.email && formik.errors.email },
{
'is-valid': formik.touched.email && !formik.errors.email,
}
)}
type='email'
name='email'
autoComplete='off'
/>
{formik.touched.email && formik.errors.email && (
<div className='fv-plugins-message-container'>
<span role='alert'>{formik.errors.email}</span>
</div>
)}
</div>
{/* end::Form group */
{/* begin::Form group */}
<div className='fv-row mb-10'>
<div className='d-flex justify-content-between mt-n5'>
<div className='d-flex flex-stack mb-2'>
{/* begin::Label */}
<label className='form-label fw-bolder text-dark fs-6 mb-0'>
Password
</label>
{/* end::Label */
/* begin::Link */}
<Link
to='/auth/forgot-password'
className='link-primary fs-6 fw-bolder'
style={{ marginLeft: '5px' }}
>
Forgot Password ?
</Link>
{/* end::Link */}
</div>
</div>
<input
type='password'
autoComplete='off'
{...formik.getFieldProps('password')}
className={clsx(
'form-control form-control-lg form-control-solid',
{
'is-invalid': formik.touched.password && formik.errors.password,
},
{
'is-valid': formik.touched.password && !formik.errors.password,
}
)}
/>
{formik.touched.password && formik.errors.password && (
<div className='fv-plugins-message-container'>
<div className='fv-help-block'>
<span role='alert'>{formik.errors.password}</span>
</div>
</div>
)}
</div>
{/* end::Form group */
{/* begin::Action */}
<div className='text-center'>
<button
type='submit'
id='kt_sign_in_submit'
className='btn btn-lg btn-primary w-100 mb-5 '
disabled={formik.isSubmitting || !formik.isValid}
>
{!loading && <span className='indicator-label'>Continue</span>}
{loading && (
<span className='indicator-progress' style={{ display: 'block' }}>
Please wait...
<span className='spinner-border spinner-border-sm align-middle ms-2'></span>
</span>
)}
</button>
{/* begin::Separator */}
<div className='text-center text-muted text-uppercase fw-bolder mb-5'>
or
</div>
{/* end::Separator */
{/* begin::Google link */}
<a className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/google-icon.svg')}
className='h-20px me-3'
/>
Continue with Google
</a>
{/* end::Google link */
{/* begin::Google link */}
<a href='#' className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/facebook-4.svg')}
className='h-20px me-3'
/>
Continue with Facebook
</a>
{/* end::Google link */
{/* begin::Google link */
<a href='#' className='btn
<details>
<summary>英文:</summary>
I can successfully login with the below code, however I don't believe react is receiving any auth data back form google to update the current user in react.
I know I am missing something here but I don't know what. Any help would be appreciated!
```/* eslint-disable jsx-a11y/anchor-is-valid */
import {useState} from 'react'
import * as Yup from 'yup'
import clsx from 'clsx'
import {Link} from 'react-router-dom'
import {useFormik} from 'formik'
import {getUserByToken, login} from '../core/_requests'
import {toAbsoluteUrl} from '../../../../_metronic/helpers'
import {useAuth} from '../core/Auth'
import {
auth,
db,
signInWithGoogle,
logInWithEmailAndPassword,
registerWithEmailAndPassword,
sendPasswordReset,
logout,
} from "../../../../firebase.js";
import { Firestore } from 'firebase/firestore'
import { useNavigate } from 'react-router-dom';
const loginSchema = Yup.object().shape({
email: Yup.string()
.email('Wrong email format')
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Email is required'),
password: Yup.string()
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Password is required'),
})
const initialValues = {
email: 'admin@demo.com',
password: 'demo',
}
/*
Formik+YUP+Typescript:
https://jaredpalmer.com/formik/docs/tutorial#getfieldprops
https://medium.com/@maurice.de.beijer/yup-validation-and-typescript-and-formik-6c342578a20e
*/
export function Login() {
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const formik = useFormik({
initialValues,
validationSchema: loginSchema,
onSubmit: async (values, { setStatus, setSubmitting }) => {
setLoading(true);
try {
await logInWithEmailAndPassword(values.email, values.password);
setLoading(false);
navigate('/dashboard');
} catch (error) {
console.error(error);
setSubmitting(false);
setLoading(false);
//setStatus(error.message);
}
},
});
return (
<form
className='form w-100'
onSubmit={formik.handleSubmit}
noValidate
id='kt_login_signin_form'
>
{/* begin::Heading */}
<div className='text-center mb-10'>
<h1 className='text-dark mb-3'>Sign In to Web Construct</h1>
<div className='text-gray-400 fw-bold fs-4'>
New Here?{' '}
<Link to='/auth/registration' className='link-primary fw-bolder'>
Create an Account
</Link>
</div>
</div>
{/* begin::Heading */}
{formik.status ? (
<div className='mb-lg-15 alert alert-danger'>
<div className='alert-text font-weight-bold'>{formik.status}</div>
</div>
) : (
<div className='mb-10 bg-light-info p-8 rounded'>
<div className='text-info'>
Use account <strong>admin@demo.com</strong> and password <strong>demo</strong> to
continue.
</div>
</div>
)}
{/* begin::Form group */}
<div className='fv-row mb-10'>
<label className='form-label fs-6 fw-bolder text-dark'>Email</label>
<input
placeholder='Email'
{...formik.getFieldProps('email')}
className={clsx(
'form-control form-control-lg form-control-solid',
{'is-invalid': formik.touched.email && formik.errors.email},
{
'is-valid': formik.touched.email && !formik.errors.email,
}
)}
type='email'
name='email'
autoComplete='off'
/>
{formik.touched.email && formik.errors.email && (
<div className='fv-plugins-message-container'>
<span role='alert'>{formik.errors.email}</span>
</div>
)}
</div>
{/* end::Form group */}
{/* begin::Form group */}
<div className='fv-row mb-10'>
<div className='d-flex justify-content-between mt-n5'>
<div className='d-flex flex-stack mb-2'>
{/* begin::Label */}
<label className='form-label fw-bolder text-dark fs-6 mb-0'>Password</label>
{/* end::Label */}
{/* begin::Link */}
<Link
to='/auth/forgot-password'
className='link-primary fs-6 fw-bolder'
style={{marginLeft: '5px'}}
>
Forgot Password ?
</Link>
{/* end::Link */}
</div>
</div>
<input
type='password'
autoComplete='off'
{...formik.getFieldProps('password')}
className={clsx(
'form-control form-control-lg form-control-solid',
{
'is-invalid': formik.touched.password && formik.errors.password,
},
{
'is-valid': formik.touched.password && !formik.errors.password,
}
)}
/>
{formik.touched.password && formik.errors.password && (
<div className='fv-plugins-message-container'>
<div className='fv-help-block'>
<span role='alert'>{formik.errors.password}</span>
</div>
</div>
)}
</div>
{/* end::Form group */}
{/* begin::Action */}
<div className='text-center'>
<button
type='submit'
id='kt_sign_in_submit'
className='btn btn-lg btn-primary w-100 mb-5 '
disabled={formik.isSubmitting || !formik.isValid}
>
{!loading && <span className='indicator-label'>Continue</span>}
{loading && (
<span className='indicator-progress' style={{display: 'block'}}>
Please wait...
<span className='spinner-border spinner-border-sm align-middle ms-2'></span>
</span>
)}
</button>
{/* begin::Separator */}
<div className='text-center text-muted text-uppercase fw-bolder mb-5'>or</div>
{/* end::Separator */}
{/* begin::Google link */}
<a className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/google-icon.svg')}
className='h-20px me-3'
/>
Continue with Google
</a>
{/* end::Google link */}
{/* begin::Google link */}
<a href='#' className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/facebook-4.svg')}
className='h-20px me-3'
/>
Continue with Facebook
</a>
{/* end::Google link */}
{/* begin::Google link */}
<a href='#' className='btn btn-flex flex-center btn-light btn-lg w-100'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/apple-black.svg')}
className='h-20px me-3'
/>
Continue with Apple
</a>
{/* end::Google link */}
</div>
{/* end::Action */}
</form>
)
}
Should firebase be sending back a token or something similar that is saved in the form of a cookie, or am I way off base?
My firebase.js
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import {
GoogleAuthProvider,
getAuth,
signInWithPopup,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
sendPasswordResetEmail,
signOut,
} from "firebase/auth";
import {
getFirestore,
query,
getDocs,
collection,
where,
addDoc,
} from "firebase/firestore";
const firebaseConfig = {
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const googleProvider = new GoogleAuthProvider();
const signInWithGoogle = async () => {
try {
const res = await signInWithPopup(auth, googleProvider);
const user = res.user;
const q = query(collection(db, "users"), where("uid", "==", user.uid));
const docs = await getDocs(q);
if (docs.docs.length === 0) {
await addDoc(collection(db, "users"), {
uid: user.uid,
name: user.displayName,
authProvider: "google",
email: user.email,
});
}
} catch (err) {
console.error(err);
alert(err.message);
}
};
const logInWithEmailAndPassword = async (email, password) => {
try {
await signInWithEmailAndPassword(auth, email, password);
} catch (err) {
console.error(err);
alert(err.message);
}
};
const registerWithEmailAndPassword = async (name, email, password) => {
try {
const res = await createUserWithEmailAndPassword(auth, email, password);
const user = res.user;
await addDoc(collection(db, "users"), {
uid: user.uid,
name,
authProvider: "local",
email,
});
} catch (err) {
console.error(err);
alert(err.message);
}
};
const sendPasswordReset = async (email) => {
try {
await sendPasswordResetEmail(auth, email);
alert("Password reset link sent!");
} catch (err) {
console.error(err);
alert(err.message);
}
};
const logout = () => {
signOut(auth);
};
export {
auth,
db,
signInWithGoogle,
logInWithEmailAndPassword,
registerWithEmailAndPassword,
sendPasswordReset,
logout,
};
</details>
# 答案1
**得分**: 1
以下是翻译好的内容:
你可能需要向我们展示在你的 `firebase.js` 中发生了什么,这是你处理身份验证的地方。
从我现在看到的情况来看,你似乎缺少了用于跟踪身份验证状态的 Firebase 钩子 - `getAuth` 和 `onAuthStateChanged`。
请参考以下文档:
https://firebase.google.com/docs/auth/web/start
https://firebase.google.com/docs/auth/web/manage-users#get_a_users_profile
以下是在 `firebase.js` 中可能执行的示例:
```js
const firebaseApp = initializeApp(firebaseConfig);
// 这获取了 FirebaseAuth 对象
const auth = getAuth(firebaseApp);
const db = getFirestore(firebaseApp);
try {
// 在这里,当您登录时,您将传递 auth 对象
const res = await signInWithEmailAndPassword(auth, email, password);
// 并且您将从成功的身份验证中返回用户
const { user } = res;
} catch (err) {
if (err instanceof Error) {
console.error(err);
alert(err.message);
} else {
console.log(err);
}
}
在 firebase.js
之外,例如在您当前的组件中:
对于您的应用程序需要有关已登录用户的信息的每个页面,都要附加一个观察者到全局身份验证对象。每当用户的登录状态发生变化时,都会调用此观察者。
使用
onAuthStateChanged
方法附加观察者。当用户成功登录时,您可以在观察者中获取有关用户的信息。
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
// 这会观察和跟踪应用程序中的身份验证状态
onAuthStateChanged(auth, (user) => {
if (user) {
// 用户已登录,查看文档以获取可用属性的列表
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// 用户已注销
// ...
}
});
这应该是连接 Firebase 和你的 React 部分之间的缺失链接 - 一旦你连接了这些点,你可以根据需要在这一侧处理它。
英文:
You'll probably have to show us what's happening in your firebase.js
where you're handling the auth.
From what i'm seeing right now, you're missing the firebase hooks that track your auth state - getAuth
and onAuthStateChanged
Refer to docs here:
https://firebase.google.com/docs/auth/web/start
https://firebase.google.com/docs/auth/web/manage-users#get_a_users_profile
Example of how you might do it in firebase.js
const firebaseApp = initializeApp(firebaseConfig);
// this gets the FirebaseAuth object
const auth = getAuth(firebaseApp);
const db = getFirestore(firebaseApp);
try {
// Here you are passing it the auth object when you sign in
const res = await signInWithEmailAndPassword(auth, email, password);
// And you get the user returned from the successful authentication
const { user } = res;
} catch (err) {
if (err instanceof Error) {
console.error(err);
alert(err.message);
} else {
console.log(err);
}
}
Outside of firebase.js
, for example in your current component:
> For each of your app's pages that need information about the signed-in
> user, attach an observer to the global authentication object. This
> observer gets called whenever the user's sign-in state changes.
>
> Attach the observer using the onAuthStateChanged method. When a user
> successfully signs in, you can get information about the user in the
> observer.
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
// This observes and tracks the state of auth in your application
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
This should be the missing link between firebase and your React side of things - then you can handle it however you need on this side once you've connected the dots.
答案2
得分: 0
你需要检查此函数调用的返回值 logInWithEmailAndPassword(values.email, values.password)
!根据响应,你要么验证用户并保存发送的令牌,要么不验证用户。
英文:
You need to check the return value of this function call
logInWithEmailAndPassword(values.email, values.password)
!
Based on the response, you either authenticate the user and save the token that's sent or do not authenticate the user.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论