reactfire icon indicating copy to clipboard operation
reactfire copied to clipboard

Can't connect to emulators when following docs

Open jhuleatt opened this issue 4 years ago • 5 comments
trafficstars

Following the docs to connect to the Firestore emulator can result in the error:

FirebaseError: Firestore has already been started and its settings can no longer be changed. You can only modify settings before calling any other methods on a Firestore object.

Originally posted by @supernaut in https://github.com/FirebaseExtended/reactfire/discussions/453

jhuleatt avatar Sep 22 '21 13:09 jhuleatt

I have been testing this more today, I don't believe there is a way to "check" that firestore has been started. The only way that I can see that would work is to request that v9 js sdk adds a 'getSettings()' to allow us to see the actual settings being used so that we can make this determination.

Example here is the original settings after app config image

Then once connectFirestoreEmulator() is called the settings are now below with a localhost image

I have setup a couple wrappers to initialize my app to make life a little easier.

The key right now was a bit of a hack, I extended Firestore to inclue _settings

interface FirestoreExt extends Firestore {
  _settings: FirestoreSettings;
}

I then check this if it matches my emulator and reinitialize if it is required.

firestore?._settings?.host != emulation.firestoreEmulatorHost

Here is my full repo example https://github.com/CodingCatDev/ccd-starter-nextjs-tailwind-firebase/blob/53074f6176d3c366b0df42aa3fc4ee16b75b2966/frontend/nextjs-tailwind/src/components/firebase/wrappers.tsx#L47

codercatdev avatar Oct 07 '21 17:10 codercatdev

Same problem here

SeverinAlexB avatar Dec 13 '21 13:12 SeverinAlexB

Not sure if this was fixed yet but here is my workaround that works so far:

/**
 * This component will connect to the emulators if the app is running in dev/test mode.
 */
export default function DetermineEmulators({
  children,
}: DetermineEmulatorsProps) {
  const app = useFirebaseApp();
  const database = getFirestore(app);
  const auth = getAuth(app);

  // Check for dev/test mode however your app tracks that.
  // `process.env.NODE_ENV` is a common React pattern
  if (process.env.NODE_ENV !== "production") {
    if (!database._settingsFrozen) {
      // Set up emulators
      connectFirestoreEmulator(database, "localhost", 8080);
      connectAuthEmulator(auth, "http://localhost:9099");
    }
  }

  return (
    <AuthProvider sdk={auth}>
      <FirestoreProvider sdk={database}>{children}</FirestoreProvider>
    </AuthProvider>
  );
}

It seems like checking if _settingsFrozen before running the connect functions works.

kyleawayan avatar Apr 01 '23 06:04 kyleawayan

I'm still getting this same error. Most of the solutions in the replies seem outdated now. I may have to give up on using ReactFire for now, as this issue doesn't seem to have gotten much attention, and I've done a ton of research on this to no avail. Various issues with the emulators have made the dev environment impossible to work with for me. If anyone does take a look at this, I’d be happy to help trying to debug the problem.

callumbirks avatar Apr 30 '23 05:04 callumbirks

I had the same problem. I am using nextjs and it seems that every time I transition from one page to another in the development environment, initialization is running. I was able to work around the problem with the following code

function FirestoreProvider({ children }) {
  const app = initializeApp(firebaseConfig);
  const functions = getFunctions(app);
  const auth = getAuth(app);
  const storage = getStorage(app);
  const db = getFirestore(app);
  if (
    process.env.NEXT_PUBLIC_ENV === "DEV" &&
    (typeof window === "undefined" || !window["_init"])
  ) {
    connectFirestoreEmulator(db, "127.0.0.1", 8080);
    connectFunctionsEmulator(functions, "127.0.0.1", 5001);
    connectAuthEmulator(auth, "http://127.0.0.1:9099", {
      disableWarnings: true,
    });
    connectStorageEmulator(storage, "127.0.0.1", 9199);
    if (typeof window !== "undefined") {
      window["_init"] = true;
    }
  }

  return (
    <FunctionsProvider sdk={functions}>
      <AuthProvider sdk={auth}>
        <ReactFireFirestoreProvider sdk={db}>
          {children}
        </ReactFireFirestoreProvider>
      </AuthProvider>
    </FunctionsProvider>
  );
}

function MyApp({ Component, pageProps }) {
  const getLayout = Component.getLayout ?? ((page) => page);

  return (
    <FirebaseAppProvider firebaseConfig={firebaseConfig}>
      <FirestoreProvider>
        {/* children */}
      </FirestoreProvider>
    </FirebaseAppProvider>
  );
}

export default MyApp;

yuki7070 avatar Jan 05 '24 10:01 yuki7070