【问题标题】:Losing data properties from firebase realtime database user collection从 firebase 实时数据库用户集合中丢失数据属性
【发布时间】:2020-01-25 09:18:59
【问题描述】:

我知道在其他情况下也有人问过这个问题。我已经阅读了这些答案。我已经尝试了答案。我已经阅读了 firebase 文档。所以,到目前为止它没有帮助。我是火力基地的新手。通过一起使用 React 和 Firebase 的教程学习如何使用该产品。为用户构建认证/授权模块。可以成功注册/注册用户并在 firebase 项目概述中的授权模块和实时数据库中查看结果。当用户注销时,对 RDb 的检查确认所有数据属性都保持不变。当用户再次登录(电子邮件/密码)时,对 RDb 的检查显示只有 uid 和电子邮件地址保留在 RDb 中。用户的非身份验证属性(如用户名和角色)变为空字符串或对象,存在但未填充。我已经大量阅读了 firebase 文档并调试了代码,但我无法确定为什么会发生这种情况,因此无法确定如何修复它。

以下是一些代码段:

首先是firebase模块:

import app from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'

... set up the config ...

// set the config constant to the appropriate value
const config = process.env.NODE_ENV === 'production' ? prodConfig : devConfig

class Firebase {
  constructor() {
    // initialize the app using the Firebase config to instatiate all of the firebase services
    app.initializeApp(config)

    // instantiate the initial auth state within the app
    this.auth = app.auth()  // Gets the Auth service for the current app

    // instantiate the database within the app
    this.db = app.database()  // Gets the Database service for the current app
    console.log('this.db ', this.db)

    // Social media login providers (authorization methods within Firebase)
    this.googleProvider = new app.auth.GoogleAuthProvider()
    this.facebookProvider = new app.auth.FacebookAuthProvider()
    this.twitterProvider = new app.auth.TwitterAuthProvider()
  }

  ... the comments are mostly for my benefit ...

  // ***** Firebase Auth API *****
  // create user with email and password (equate doCreate... with Firebase createUser...)
  doCreateUserWithEmailAndPassword = ( email, password ) => this.auth.createUserWithEmailAndPassword( email, password )
  // ***** SignIn with email ***** (equate doSignIn... with Firebase signIn...)
  doSignInWithEmailAndPassword = ( email, password ) => this.auth.signInWithEmailAndPassword( email, password )
  // ***** SignIn with facebook ***** (equate as above)
  doSignInWithFacebook = () => this.auth.signInWithPopup( this.facebookProvider )
  // ***** SignIn with google ***** (equate as above)
  doSignInWithGoogle = () => this.auth.signInWithPopup( this.googleProvider )
  // ***** SignIn with twitter ***** (equate as above)
  doSignInWithTwitter = () => this.auth.signInWithPopup( this.twitterProvider )
  // ***** SignOut ***** (equate as above)
  doSignOut = () => this.auth.signOut()
  // ***** Password Reset ***** (equate as above)
  doPasswordReset = email => this.auth.sendPasswordResetEmail( email )
  // ***** Password Update ***** (equate as above)
  doPasswordUpdate = password => this.auth.currentUser.updatePassword( password )

  // ***** User API ***** 
  // set the current user id
  user = uid => this.db.ref(`users/${uid}`)
  // set the reference to the users collection in the firebase database
  users = () =>this.db.ref('users')

  // ***** Merge Auth and DB User API *****

  ... big block of comments to me ...

  onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(authUser => {  
      if ( authUser ) {                         
        this.user(authUser.uid)                 
          .once('value')                        
          .then(snapshot => {                          
            const dbUser = snapshot.val()
            console.log('this.user ',this.user)    
            console.log('dbUser ',dbUser)
            // default empty roles
            if ( !dbUser.roles ) {
              dbUser.roles = {}
            }
            // merge auth and db user
            this.db.ref(`users/${this.user}`).set({
              uid: authUser.uid,
              email: authUser.email,
              username: authUser.username,
              roles: authUser.roles,
              ...dbUser,
            })
            console.log('firebase.js authUser ', authUser)
            next(authUser)
          })
      } else {                                  //  there is no authUser
        fallback()
      }
    })
}

export default Firebase

这是注册组件:

// index.js - SignUp
//  the entry point for the SignUp component
import React, { Component } from 'react'
import { Link, withRouter } from 'react-router-dom'
import { compose } from 'recompose'

import { Typography, Input, Checkbox, FormLabel, Button } from '@material-ui/core'

import { withFirebase } from '../Firebase'
import * as ROUTES from '../../constants/routes'
import * as ROLES from '../../constants/roles'

import '../../styles/auth.css'

const SignUpPage = () => (
  <div id='wrapper' className='signup-page'>
    <SignUpForm />
  </div>
)

// initialize the state of the component using destructuring
// allows INITIAL_STATE to be reset after successful SignUp
const INITIAL_STATE = {
  username: '',
  email: '',
  passwordOne: '',
  passwordTwo: '',
  isAdmin: false,
  error: null,
}

class SignUpFormBase extends Component {
  constructor(props) {
    super(props)

    // spread operator (...) spreads out to reach all properties individually
    this.state = { ...INITIAL_STATE }
  }

  onSubmit = event => {
    // get necessary info from this.state to pass to the Firebase authentication API
    const { username, email, passwordOne, isAdmin } = this.state
    const roles = {}
    if ( isAdmin ) { roles[ROLES.ADMIN] = ROLES.ADMIN } //  set roles if the admin checkbox is checked

    this.props.firebase
      // create a user (limited access) in the authentication database
      .doCreateUserWithEmailAndPassword( email, passwordOne )
      // successful 
      .then( authUser => {
        // create a user in Firebase realtime database -- this is where you manage user properties
        // because in the firebase auth module, users cannot be manipulated.
        console.log('signup authUser ',authUser)
        return this.props.firebase
          .user(authUser.user.uid)          //  use the authUser.uid to:
          .set({ username, email, roles })  //  write username, email & roles to the rdb
      })
      .then(() => {
        // update state and redirect to Home page
        this.setState({ ...INITIAL_STATE })
        this.props.history.push(ROUTES.HOME)
      })
      // error - setState, error (if something is wrong)
      .catch(error => {
        this.setState({ error })
      })
    // prevent default behavior (a reload of the browser)
    event.preventDefault()
  }

  onChange = event => {
    // dynamically set state properties when they change, based on which input call is executed
    // each <input> element (in the return) operates on a different property of state (according to value)
    this.setState({ [event.target.name]: event.target.value })
  }

  onChangeCheckbox = event => {
    this.setState({ [event.target.name]: event.target.checked })
  }

  render() {
    // parse each of the values from current state
    const {
      username,
      email,
      passwordOne,
      passwordTwo,
      isAdmin,
      error
    } = this.state
    // list of invalid conditions for which to check (validation of form elements)
    const isInvalid =
      passwordOne !== passwordTwo ||
      passwordOne === '' ||
      email === '' ||
      username === ''
    return (
      // the input form -- with fields (username, email, passwordOne, passwordTwo)
      <div className='container'>
        <Typography 
          variant='h6' 
          align = 'center' 
          className='item'
        >
          Sign Up Page
        </Typography>
        <br />
        <form className='signup-form item' onSubmit={ this.onSubmit }>
          <Input
            className='item'
            name='username'
            value={username}
            onChange={this.onChange}
            type='text'
            placeholder='Full Name'
          />

... input email, password, confirm password, checkbox to designate Admin ...

          <Button
            variant='contained'
            className='item btn btn-secondary' 
            disabled={ isInvalid } 
            type='submit'
          >
            Sign Up
          </Button>

          {/* if there is an error (a default Firebase property), render the error message */}
          {error && <p>{ error.message }</p>}
        </form>
      </div>
    )
  }
}

const SignUpLink = () => (
  <Typography 
    variant = 'body1' 
    align = 'center' 
    className = 'item' 
  >
    Don't have an account? <Link to={ ROUTES.SIGN_UP }>Sign Up</Link>
  </Typography>
)

const SignUpForm = compose(withRouter, withFirebase)(SignUpFormBase)

export default SignUpPage

export { SignUpForm, SignUpLink }

这里是登录(电子邮件/密码)组件:

// SignInEmail.js - SignIn
//  the email/password SignIn component
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { compose } from 'recompose'

import { Typography, Input } from '@material-ui/core'
import Button from 'react-bootstrap/Button'

import { withFirebase } from '../Firebase'
import * as ROUTES from '../../constants/routes'

// initialize the state of the component using destructuring
// allows INITIAL_STATE to be reset after successful SignUp
const INITIAL_STATE = {
  username: '',
  email: '',
  password: '',
  roles: {},
  error: null,
}

// ======================================
// ***** signin with email/password *****
// ======================================
class SignInEmailBase extends Component {
  constructor(props) {
    super(props)

    // spread operator (...) spreads out to reach all properties individually
    this.state = {
      ...INITIAL_STATE
    }
  }

  onSubmit = event => {
    // get necessary info from this.state to pass to the Firebase authentication API
    // const { username, email, password, roles } = this.state
    const { username, email, password, roles } = this.state

    this.props.firebase
      // execute SignIn function (create a user)
      .doSignInWithEmailAndPassword( email, password )
      // successful 
      .then(authUser => {
        // create a user in Firebase Realtime database
        console.log('signin authUser ',authUser)
        return this.props.firebase
          .user(authUser.user.uid)
          .set({ username, email, roles })
      })
      .then(() => {
        // update state and redirect to Home page
        this.setState({ ...INITIAL_STATE })
        this.props.history.push(ROUTES.HOME)
      })
      // error - setState, error (if something is wrong)
      .catch(error => {
        this.setState({
          error
        })
      })
    // prevent default behavior (a reload of the browser)
    event.preventDefault()
  }

  onChange = event => {
    // dynamically set state properties when they change, based on which input call is executed
    // each <input> element (in the return) operates on a different property of state (according to value)
    this.setState({ [event.target.name]: event.target.value })
  }

  render() {
    // parse each of the values from current state
    const { email, password, error } = this.state

    // list of invalid conditions for which to check (validation of form elements)
    const isInvalid = password === '' || email === ''

    return (
      // the input form -- with fields (username, email, passwordOne, passwordTwo)
      <div className = 'container signin-page'>
        <Typography 
          variant = 'h6' 
          align = 'center' 
          className = 'item' 
        >
          Sign In Page
        </Typography> 
        <br />
        <form className = 'item email-form' onSubmit = { this.onSubmit} >

          ... input email, password...

          { /* disable the button if the form is invalid -- see isInvalid above */ } 
          <Button 
            className = 'item btn btn-secondary' 
            type = 'submit' 
            disabled = { isInvalid } 
          >
            SIGN IN WITH EMAIL
          </Button>

          { /* if there is an error (a default Firebase property), render the error message */ }
          { error && <p> { error.message } </p> } 
        </form>
      </div>
    )
  }
}

const SignInEmail = compose(withRouter, withFirebase, )(SignInEmailBase)

export default SignInEmail

这是浏览器检查的屏幕截图和注册后实时数据库条目的屏幕截图

这是退出和登录后的同一个用户——没有其他数据操作

我当然需要一些帮助。谢谢。

【问题讨论】:

    标签: reactjs firebase firebase-realtime-database firebase-authentication


    【解决方案1】:

    我自己偶然发现了这个问题的答案。当我查看我的代码并将 SignUp 组件与 SignIn 组件进行比较时,我意识到在 SignIn 组件中看起来不合适。就是 onSubmit 块中的这个块:

    this.props.firebase
          // execute SignIn function (create a user)
          .doSignInWithEmailAndPassword( email, password )
          // successful 
          .then(authUser => {
            // create a user in Firebase Realtime database
            return this.props.firebase
              .user(authUser.user.uid)
              .set({ username, email, roles })
          })
          .then(() => {
            // update state and redirect to Home page
            this.setState({ ...INITIAL_STATE })
            this.props.history.push(ROUTES.HOME)
          })
          // error - setState, error (if something is wrong)
          .catch(error => {
            this.setState({
              error
            })
          })
    

    它与 signUp 组件中的类似块相同。注册是必要的,因为我正在创建一个具有注册的用户,并且正在将在 signIn 组件中消失的信息输入到 signUp 组件中,因此它可以在 authUser 中临时用于传输到 RdB。该信息未在登录组件中输入,因此无法传输到实时数据库,因此(因为 firebase 在身份验证模块和 RdB 之间同步用户的工作方式)这些属性被空字符串覆盖或空对象。此外,由于该函数是一个 signIn 函数,因此无需在 signIn 时间更新 RdB,因此解决方案是在 signIn 组件中消除此代码块。

          .then(authUser => {
            // create a user in Firebase Realtime database
            return this.props.firebase
              .user(authUser.user.uid)
              .set({ username, email, roles })
          })
    

    抱歉,打扰了。但我确实想发帖说我想通了。

    【讨论】:

      猜你喜欢
      • 2020-10-28
      • 2020-01-17
      • 2019-02-26
      • 2021-04-19
      • 2021-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多