Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signOut fails due to a readonly property in Auth #5722

Closed
theninthsky opened this issue Nov 11, 2021 · 2 comments
Closed

signOut fails due to a readonly property in Auth #5722

theninthsky opened this issue Nov 11, 2021 · 2 comments

Comments

@theninthsky
Copy link

[REQUIRED] Describe your environment

  • Operating System version: macOS Big Sur
  • Browser version: Chrome 95
  • Firebase SDK version: 9
  • Firebase Product: auth

[REQUIRED] Describe the problem

Calling signOut throws an error.

The error that is thrown:

TypeError: Cannot assign to read only property 'currentUser' of object '#<AuthImpl>'
    at AuthImpl.directlySetCurrentUser

Relevant Code:

firebase-app.js

.
.
.
const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)

User.jsx

import { signOut } from 'firebase/auth'
import { auth } from 'firebase-app'
.
.
.
signOut(auth)

You can see the entire code here:
https://github.com/theninthsky/flyingnotes-client/tree/firebase

Thanks.

@hsubox76
Copy link
Contributor

Hi, the problem actually seems to be your recoil library, which somehow mutates the user object to make something on it immutable (it probably makes a shallow clone and mutates deeper properties)? I made a minimal reproduction by replacing your src/index.js file with the following, which isolates the problem to a single file. If you replace useRecoilState with the regular react useState the errors about not being able to change a read-only object seem to go away. I'm not sure if it's a bug with recoil or working as intended, you can ask if you're not sure. In the meantime maybe make a copy of the user object returned by onAuthStateChanged and give that copy to setUser. I think probably recoil shouldn't do that and it's a bug? But they would know better.

Anyway, the first snippet is the reproduction (replace src/index.js with this) and the second snippet is an example of how making a deep copy in onAuthStateChanged can avoid that problem. I wouldn't use JSON.parse/JSON.stringify in production, I just wanted to see if that fixed it without having to import another library to deep copy.

import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
import { initializeApp } from 'firebase/app'
import { getAuth, signOut, onAuthStateChanged, signInAnonymously } from 'firebase/auth'

import 'service-worker-registration'
import 'normalize.css'
import 'styles/_globals.scss'
import { BrowserRouter } from 'react-router-dom'
import { RecoilRoot, useRecoilState, atom } from 'recoil'
import { ErrorBoundary } from 'react-error-boundary'

const firebaseConfig = {
  apiKey: 'AIzaSyB3BYpqf_FrZ2WQidSh9Ml04kuXJp3fvVk',
  authDomain: 'chholland-test.firebaseapp.com',
  databaseURL: 'https://chholland-test.firebaseio.com',
  projectId: 'chholland-test',
  storageBucket: 'chholland-test.appspot.com',
  messagingSenderId: '91336787373',
  appId: '1:91336787373:web:a3dffe45ec797267',
  measurementId: 'G-RV2DRJVZ88'
}

const app = initializeApp(firebaseConfig)

ReactDOM.render(
  <RecoilRoot>
    <BrowserRouter>
      <ErrorBoundary
        fallback={<div>An error occured, please reload the app.</div>}
        onError={() => localStorage.clear()}
      >
  <Test />
    </ErrorBoundary>
  </BrowserRouter>
</RecoilRoot>, document.getElementById('root'))

function Test() {
  const [user, setUser] = useRecoilState(atom({
    key: 'userState',
    default: null
  }))
  useEffect(() => {
    onAuthStateChanged(getAuth(app), user => setUser(user))
  }, [setUser])
  return (
    <div>
      <div>{user ? user.uid : 'no user'}</div>
      <button onClick={() => signInAnonymously(getAuth(app))}>sign in</button>
      <button onClick={() => signOut(getAuth(app))}>sign out</button>
    </div>
  )
}
  useEffect(() => {
    onAuthStateChanged(getAuth(app), user => {
      const userCopy = JSON.parse(JSON.stringify(user));
      setUser(userCopy);
    })
  }, [setUser])

@theninthsky
Copy link
Author

Wow! thank you so much for the deep analysis.
I will surely open an issue in recoil's repo regarding the matter.
I'll be making a copy of the user object before passing it to recoil in the meantime.
Thanks again :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants