FirebaseExtended / reactfire

Hooks, Context Providers, and Components that make it easy to interact with Firebase.
https://firebaseopensource.com/projects/firebaseextended/reactfire/
MIT License
3.52k stars 401 forks source link

Can't connect to emulators when following docs #459

Open jhuleatt opened 2 years ago

jhuleatt commented 2 years ago

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

codercatdev commented 2 years ago

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

SeverinAlexB commented 2 years ago

Same problem here

kyleawayan commented 1 year ago

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.

callumbirks commented 1 year ago

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.

yuki7070 commented 8 months ago

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;