You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Many of the firestore objects have enumerable/public properties that result in circular references when deeply scanning through these objects, for example when JSON.stringify a DocumentReference. The JSON example is just the most common deep scan method that might be applied to the object, there are other cases where deep scanning is used that can run foul of these circular references.
In our specific application we're using Vue + Vuex + Firestore. We store the firestore documents in our vuex store which exposes these objects to the Vue reactivity system. While this works most of the time there are a few standard corner cases where cyclic references cause issues for us
Deep watchers
If we have watchers that observe any object that contains a firestore reference then this causes the Vue reactivity system to stack overflow while it follows the infinite loop.
Vue(x) Dev Tools
When passing data between the page context and the dev tools/plugin context the Vue dev tools plugin attempts to serialise the Vuex state to a string using JSON.stringify. If the state contains any firestore objects with circular references then this fails and the plugin falls back to a slower, more memory intensive, json process that deals with this. This causes the vue dev tools to crash frequently with out-of-memory. It's totally understandable that this could be a Vue dev tools issue, but it could also be solved by removing circular references in firebase.
Steps to reproduce:
Try to JSON.stringify any of the following directly, or objects that have properties that reference these:
firebase.firestore.DocumentReference
firebase.firestore.CollectionReference
firebase.firestore.Query
There are probably plenty of others, but these are the ones that have been spotted in our app.
Relevant Code:
constapp=firebase.initialiseApp();constdb=app.firestore();constdocRef=db.doc('docs/doc');JSON.stringify(docRef);// an example of walking the object tree// Outputs: Uncaught TypeError: Converting circular structure to JSON// --> starting at object with constructor 'FirebaseAppImpl'// | property 'firebase_' -> object with constructor 'Object'// | property 'apps' -> object with constructor 'Array'// --- index 0 closes the circle// at JSON.stringify (<anonymous>)
Places I've noticed circular references
The apps list in FirebaseAppImpl.firebase_.apps has a reference to the enclosing FirebaseAppImpl
The providers map in FirebaseAppImpl.container.providers contains a provider for the app that keeps track of the instance that contains the providers.
There might be others.
Working around the issue
In our app we've had to work around the issue in some very specific ways.
We deep scan all firestore objects before they get put into our store and Object.defineProperty(..., {enumerable: false}) any DocumentReference.* we find. This fixes the vast majority of cases but has a performance impact and is flaky
We are careful to avoid exposing non-required firestore objects to vue or vuex. This is harder than you'd think as even arguments to mutators in vuex count as exposure
The text was updated successfully, but these errors were encountered:
@mattnathan Thanks for filing such a detailed issue as well as your workaround!
Firestore classes like DocumentReference, Query, and CollectionReference hold references to the Firestore class. The Firestore class has a cyclic dependency with FirebaseApp, which creates the error you see when calling JSON.stringify. I'll investigate how to break this cycle next week, and keep you updated!
[REQUIRED] Describe your environment
[REQUIRED] Describe the problem
Many of the firestore objects have enumerable/public properties that result in circular references when deeply scanning through these objects, for example when
JSON.stringify
a DocumentReference. The JSON example is just the most common deep scan method that might be applied to the object, there are other cases where deep scanning is used that can run foul of these circular references.In our specific application we're using Vue + Vuex + Firestore. We store the firestore documents in our vuex store which exposes these objects to the Vue reactivity system. While this works most of the time there are a few standard corner cases where cyclic references cause issues for us
Deep watchers
If we have watchers that observe any object that contains a firestore reference then this causes the Vue reactivity system to stack overflow while it follows the infinite loop.
Vue(x) Dev Tools
When passing data between the page context and the dev tools/plugin context the Vue dev tools plugin attempts to serialise the Vuex state to a string using
JSON.stringify
. If the state contains any firestore objects with circular references then this fails and the plugin falls back to a slower, more memory intensive, json process that deals with this. This causes the vue dev tools to crash frequently with out-of-memory. It's totally understandable that this could be a Vue dev tools issue, but it could also be solved by removing circular references in firebase.Steps to reproduce:
Try to
JSON.stringify
any of the following directly, or objects that have properties that reference these:There are probably plenty of others, but these are the ones that have been spotted in our app.
Relevant Code:
Places I've noticed circular references
apps
list inFirebaseAppImpl.firebase_.apps
has a reference to the enclosingFirebaseAppImpl
providers
map inFirebaseAppImpl.container.providers
contains a provider for the app that keeps track of the instance that contains the providers.There might be others.
Working around the issue
In our app we've had to work around the issue in some very specific ways.
Object.defineProperty(..., {enumerable: false})
anyDocumentReference.*
we find. This fixes the vast majority of cases but has a performance impact and is flakyThe text was updated successfully, but these errors were encountered: