Потеря свойств данных из пользовательской коллекции базы данных Firebase в реальном времени - PullRequest
0 голосов
/ 24 января 2020

Я знаю, что этот вопрос задавался в других контекстах. Я прочитал эти ответы. Я попробовал ответы. Я прочитал документы по Firebase. Так что пока это не помогает. Я новичок в пожарной части. Изучите, как использовать продукт, изучив руководство по совместному использованию React и Firebase. Создание модуля аутентификации / авторизации для пользователей. Может успешно зарегистрировать / зарегистрировать пользователя и увидеть результаты как в модуле авторизации, так и в базе данных в реальном времени в обзоре проекта Firebase. Когда пользователь выходит из системы, проверка RDb подтверждает, что все свойства данных сохраняются. Когда пользователь снова входит в систему (электронная почта / пароль), проверка RDb показывает, что в RDb сохраняются только uid и адрес электронной почты. Неавторизованные свойства пользователя, такие как имя пользователя и роль, становятся пустыми строками или объектами, присутствующими, но не заполненными. Я провел значительное чтение документации по 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

Вот компонент Sign UP:

// 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 }

Вот компонент Sign IN (электронная почта / пароль):

// 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

Вот скриншот просмотра браузера и скриншот записи базы данных в реальном времени после регистрации inspect after sign up

Rdb after sign up

Здесь тот же пользователь после выхода и входа - никаких других манипуляций с данными

inspect same user after sign out and sign in

Rdb same user after sign out sign in

Я уверен, что мог бы помочь. Спасибо.

1 Ответ

0 голосов
/ 25 января 2020

Я вроде наткнулся на ответ на этот вопрос сам. Когда я просматривал свой код и сравнивал компонент 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 работает при синхронизации пользователей между модулем auth и RdB) эти свойства были перезаписаны пустыми строками. или пустые объекты. Кроме того, поскольку функция является функцией входа в систему, во время входа в систему нет необходимости обновлять RdB, поэтому решение состоит в том, чтобы устранить этот блок кода в компоненте входа в.

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

Извините, если я был беспокоить. Но я действительно хотел опубликовать, что я понял это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...