如何在我的React应用中从Firebase身份验证中更新状态。

huangapple go评论69阅读模式
英文:

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&#39;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&#39;t know what. Any help would be appreciated!

```/* eslint-disable jsx-a11y/anchor-is-valid */
import {useState} from &#39;react&#39;
import * as Yup from &#39;yup&#39;
import clsx from &#39;clsx&#39;
import {Link} from &#39;react-router-dom&#39;
import {useFormik} from &#39;formik&#39;
import {getUserByToken, login} from &#39;../core/_requests&#39;
import {toAbsoluteUrl} from &#39;../../../../_metronic/helpers&#39;
import {useAuth} from &#39;../core/Auth&#39;
import {
  auth,
  db,
  signInWithGoogle,
  logInWithEmailAndPassword,
  registerWithEmailAndPassword,
  sendPasswordReset,
  logout,
} from &quot;../../../../firebase.js&quot;;
import { Firestore } from &#39;firebase/firestore&#39;
import { useNavigate } from &#39;react-router-dom&#39;;



const loginSchema = Yup.object().shape({
  email: Yup.string()
    .email(&#39;Wrong email format&#39;)
    .min(3, &#39;Minimum 3 symbols&#39;)
    .max(50, &#39;Maximum 50 symbols&#39;)
    .required(&#39;Email is required&#39;),
  password: Yup.string()
    .min(3, &#39;Minimum 3 symbols&#39;)
    .max(50, &#39;Maximum 50 symbols&#39;)
    .required(&#39;Password is required&#39;),
})

const initialValues = {
  email: &#39;admin@demo.com&#39;,
  password: &#39;demo&#39;,
}

/*
  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 }) =&gt; {
      setLoading(true);
      try {
        await logInWithEmailAndPassword(values.email, values.password);
        setLoading(false);
        navigate(&#39;/dashboard&#39;);
      } catch (error) {
        console.error(error);
        setSubmitting(false);
        setLoading(false);
        //setStatus(error.message);
      }
    },
  });

  return (
    &lt;form
      className=&#39;form w-100&#39;
      onSubmit={formik.handleSubmit}
      noValidate
      id=&#39;kt_login_signin_form&#39;
    &gt;
      {/* begin::Heading */}
      &lt;div className=&#39;text-center mb-10&#39;&gt;
        &lt;h1 className=&#39;text-dark mb-3&#39;&gt;Sign In to Web Construct&lt;/h1&gt;
        &lt;div className=&#39;text-gray-400 fw-bold fs-4&#39;&gt;
          New Here?{&#39; &#39;}
          &lt;Link to=&#39;/auth/registration&#39; className=&#39;link-primary fw-bolder&#39;&gt;
            Create an Account
          &lt;/Link&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      {/* begin::Heading */}

      {formik.status ? (
        &lt;div className=&#39;mb-lg-15 alert alert-danger&#39;&gt;
          &lt;div className=&#39;alert-text font-weight-bold&#39;&gt;{formik.status}&lt;/div&gt;
        &lt;/div&gt;
      ) : (
        &lt;div className=&#39;mb-10 bg-light-info p-8 rounded&#39;&gt;
          &lt;div className=&#39;text-info&#39;&gt;
            Use account &lt;strong&gt;admin@demo.com&lt;/strong&gt; and password &lt;strong&gt;demo&lt;/strong&gt; to
            continue.
          &lt;/div&gt;
        &lt;/div&gt;
      )}

      {/* begin::Form group */}
      &lt;div className=&#39;fv-row mb-10&#39;&gt;
        &lt;label className=&#39;form-label fs-6 fw-bolder text-dark&#39;&gt;Email&lt;/label&gt;
        &lt;input
          placeholder=&#39;Email&#39;
          {...formik.getFieldProps(&#39;email&#39;)}
          className={clsx(
            &#39;form-control form-control-lg form-control-solid&#39;,
            {&#39;is-invalid&#39;: formik.touched.email &amp;&amp; formik.errors.email},
            {
              &#39;is-valid&#39;: formik.touched.email &amp;&amp; !formik.errors.email,
            }
          )}
          type=&#39;email&#39;
          name=&#39;email&#39;
          autoComplete=&#39;off&#39;
        /&gt;
        {formik.touched.email &amp;&amp; formik.errors.email &amp;&amp; (
          &lt;div className=&#39;fv-plugins-message-container&#39;&gt;
            &lt;span role=&#39;alert&#39;&gt;{formik.errors.email}&lt;/span&gt;
          &lt;/div&gt;
        )}
      &lt;/div&gt;
      {/* end::Form group */}

      {/* begin::Form group */}
      &lt;div className=&#39;fv-row mb-10&#39;&gt;
        &lt;div className=&#39;d-flex justify-content-between mt-n5&#39;&gt;
          &lt;div className=&#39;d-flex flex-stack mb-2&#39;&gt;
            {/* begin::Label */}
            &lt;label className=&#39;form-label fw-bolder text-dark fs-6 mb-0&#39;&gt;Password&lt;/label&gt;
            {/* end::Label */}
            {/* begin::Link */}
            &lt;Link
              to=&#39;/auth/forgot-password&#39;
              className=&#39;link-primary fs-6 fw-bolder&#39;
              style={{marginLeft: &#39;5px&#39;}}
            &gt;
              Forgot Password ?
            &lt;/Link&gt;
            {/* end::Link */}
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;input
          type=&#39;password&#39;
          autoComplete=&#39;off&#39;
          {...formik.getFieldProps(&#39;password&#39;)}
          className={clsx(
            &#39;form-control form-control-lg form-control-solid&#39;,
            {
              &#39;is-invalid&#39;: formik.touched.password &amp;&amp; formik.errors.password,
            },
            {
              &#39;is-valid&#39;: formik.touched.password &amp;&amp; !formik.errors.password,
            }
          )}
        /&gt;
        {formik.touched.password &amp;&amp; formik.errors.password &amp;&amp; (
          &lt;div className=&#39;fv-plugins-message-container&#39;&gt;
            &lt;div className=&#39;fv-help-block&#39;&gt;
              &lt;span role=&#39;alert&#39;&gt;{formik.errors.password}&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        )}
      &lt;/div&gt;
      {/* end::Form group */}

      {/* begin::Action */}
      &lt;div className=&#39;text-center&#39;&gt;
        &lt;button
          
          type=&#39;submit&#39;
          id=&#39;kt_sign_in_submit&#39;
          className=&#39;btn btn-lg btn-primary w-100 mb-5 &#39;
          disabled={formik.isSubmitting || !formik.isValid}
        &gt;
          {!loading &amp;&amp; &lt;span className=&#39;indicator-label&#39;&gt;Continue&lt;/span&gt;}
          {loading &amp;&amp; (
            &lt;span className=&#39;indicator-progress&#39; style={{display: &#39;block&#39;}}&gt;
              Please wait...
              &lt;span className=&#39;spinner-border spinner-border-sm align-middle ms-2&#39;&gt;&lt;/span&gt;
            &lt;/span&gt;
          )}
        &lt;/button&gt;

        {/* begin::Separator */}
        &lt;div className=&#39;text-center text-muted text-uppercase fw-bolder mb-5&#39;&gt;or&lt;/div&gt;
        {/* end::Separator */}

        {/* begin::Google link */}
        &lt;a className=&#39;btn btn-flex flex-center btn-light btn-lg w-100 mb-5&#39;&gt;
          &lt;img
            alt=&#39;Logo&#39;
            src={toAbsoluteUrl(&#39;/media/svg/brand-logos/google-icon.svg&#39;)}
            className=&#39;h-20px me-3&#39;
          /&gt;
          Continue with Google
        &lt;/a&gt;
        {/* end::Google link */}

        {/* begin::Google link */}
        &lt;a href=&#39;#&#39; className=&#39;btn btn-flex flex-center btn-light btn-lg w-100 mb-5&#39;&gt;
          &lt;img
            alt=&#39;Logo&#39;
            src={toAbsoluteUrl(&#39;/media/svg/brand-logos/facebook-4.svg&#39;)}
            className=&#39;h-20px me-3&#39;
          /&gt;
          Continue with Facebook
        &lt;/a&gt;
        {/* end::Google link */}

        {/* begin::Google link */}
        &lt;a href=&#39;#&#39; className=&#39;btn btn-flex flex-center btn-light btn-lg w-100&#39;&gt;
          &lt;img
            alt=&#39;Logo&#39;
            src={toAbsoluteUrl(&#39;/media/svg/brand-logos/apple-black.svg&#39;)}
            className=&#39;h-20px me-3&#39;
          /&gt;
          Continue with Apple
        &lt;/a&gt;
        {/* end::Google link */}
      &lt;/div&gt;
      {/* end::Action */}
    &lt;/form&gt;
  )
}

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 &#39;firebase/compat/auth&#39;;
import &#39;firebase/compat/firestore&#39;;
import {
GoogleAuthProvider,
getAuth,
signInWithPopup,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
sendPasswordResetEmail,
signOut,
} from &quot;firebase/auth&quot;;
import {
getFirestore,
query,
getDocs,
collection,
where,
addDoc,
} from &quot;firebase/firestore&quot;;
const firebaseConfig = {
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const googleProvider = new GoogleAuthProvider();
const signInWithGoogle = async () =&gt; {
try {
const res = await signInWithPopup(auth, googleProvider);
const user = res.user;
const q = query(collection(db, &quot;users&quot;), where(&quot;uid&quot;, &quot;==&quot;, user.uid));
const docs = await getDocs(q);
if (docs.docs.length === 0) {
await addDoc(collection(db, &quot;users&quot;), {
uid: user.uid,
name: user.displayName,
authProvider: &quot;google&quot;,
email: user.email,
});
}
} catch (err) {
console.error(err);
alert(err.message);
}
};
const logInWithEmailAndPassword = async (email, password) =&gt; {
try {
await signInWithEmailAndPassword(auth, email, password);
} catch (err) {
console.error(err);
alert(err.message);
}
};
const registerWithEmailAndPassword = async (name, email, password) =&gt; {
try {
const res = await createUserWithEmailAndPassword(auth, email, password);
const user = res.user;
await addDoc(collection(db, &quot;users&quot;), {
uid: user.uid,
name,
authProvider: &quot;local&quot;,
email,
});
} catch (err) {
console.error(err);
alert(err.message);
}
};
const sendPasswordReset = async (email) =&gt; {
try {
await sendPasswordResetEmail(auth, email);
alert(&quot;Password reset link sent!&quot;);
} catch (err) {
console.error(err);
alert(err.message);
}
};
const logout = () =&gt; {
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 &quot;firebase/auth&quot;;

const auth = getAuth();

// This observes and tracks the state of auth in your application
onAuthStateChanged(auth, (user) =&gt; {
  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.

huangapple
  • 本文由 发表于 2023年2月14日 08:32:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75442445.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定