Firebaseui-web: React Error: An AuthUI instance already exists for the key "[DEFAULT]"

Created on 25 Mar 2018  路  6Comments  路  Source: firebase/firebaseui-web

Apparently, the problem is due to the ui.delete() command being not fast enough.
Notice below that the "delete" is resolved after a new instance's constructor was fired already.

image

My code

import React from 'react'
import firebase from 'firebase'
import firebaseui from 'firebaseui'
import { Redirect } from 'react-router-dom';

import { auth } from '~/firebase'


export default class SignIn extends React.Component {
  state = {
    uiShown: false,
    redirect: false
  }

  ui // = new firebaseui.auth.AuthUI(auth)

  uiConfig = { ... }

  constructor(props) {
    super(props)
    console.debug('constructor')

    this.ui = new firebaseui.auth.AuthUI(auth)
  }

  componentDidMount() {
    this.ui.start('#firebaseui-auth-container', this.uiConfig)
  }

  componentWillUnmount() {
    console.debug('will unmount')
    this.ui.delete().then(() => console.debug('deleted'))
  }

  render() {
    return (
      <>
        <div id="firebaseui-auth-container"/>
        {!this.state.uiShown && <div id="loader">Loading...</div>}
        {this.state.redirect && <Redirect to="/dashboard"/>}
      </>
    )
  }
}

My app layout is such that I have some public and private pages. If private page is accessed when user is not logged in, he gets redirected to /signin page. The problem is when I am on /signin page and I access one of the other private pages - I am redirected back immediately and the tree containing the SignIn component gets re-mounted.

Most helpful comment

You could also keep a static reference to the delete promise. something like:

```js
export default class SignIn extends React.Component {

// ...

// Resolved unless an instance of FirebaseUI is being deleted.
static firebaseUiDeleted = Promise.resolve();

componentDidMount() {
SignIn.firebaseUiDeleted.then(() => {
this.ui = new firebaseui.auth.AuthUI(auth)
this.ui.start('#firebaseui-auth-container', this.uiConfig)
})
}

componentWillUnmount() {
SignIn.firebaseUiDeleted = this.ui.delete()
}
}

All 6 comments

delete returns a promise which resolves on deletion. You have to wait for it to resolve before you initialize.

Right, but why is that asynchronous? That doesn't go well with react lifecycle methods. I will have to keep a global reference to the promise then I guess.

Is there a better suggested approach?

Take a look at the code. It is open sourced: https://github.com/firebase/firebaseui-web/blob/master/javascript/widgets/authui.js#L812
We call signOut underneath on an internal Auth instance. The operation is asynchronous. Nothing we can do about it.

Thank you for clarification

I was able to come around this issue with pushing the instantiation at the end of execution queue.

  componentDidMount() {
    setTimeout(() => {
      this.ui = new firebaseui.auth.AuthUI(auth)
      this.ui.start('#firebaseui-auth-container', this.uiConfig)
    }, 0)
  }

  componentWillUnmount() {
    this.ui.delete()
  }

You could also keep a static reference to the delete promise. something like:

```js
export default class SignIn extends React.Component {

// ...

// Resolved unless an instance of FirebaseUI is being deleted.
static firebaseUiDeleted = Promise.resolve();

componentDidMount() {
SignIn.firebaseUiDeleted.then(() => {
this.ui = new firebaseui.auth.AuthUI(auth)
this.ui.start('#firebaseui-auth-container', this.uiConfig)
})
}

componentWillUnmount() {
SignIn.firebaseUiDeleted = this.ui.delete()
}
}

Was this page helpful?
0 / 5 - 0 ratings