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

Reactfire preventing lazy loading of firebase modules #488

Open matt-kinton opened 2 years ago

matt-kinton commented 2 years ago

When using ReactFire I want to lazy load Firestore to reduce build initial build size and improve load times. When using ReactFire lazy loading firestore does not happen even while using import('firebase/firestore').

I have included a version that does not use ReactFire with working lazy loading as shown by the initialisation time. I have also included a version that does use ReactFire where I am trying to use lazy loading but I believe it is not working.

I noticed this in a Nextjs project with the bundle analyzer but haven't included Nextjs in the examples for simplicity.

Version info

React: 17.02

Firebase: 9.4.1

ReactFire: 4.2.1

Test case

Test case without using ReactFire and lazy loading working: https://codesandbox.io/s/blissful-scott-05qj6?file=/src/App.tsx https://csb-9c5hc.netlify.app/

Test case with using ReactFire and lazy loading not working: https://codesandbox.io/s/elegant-roentgen-2iyt8?file=/src/App.tsx https://csb-2iyt8.netlify.app/

You can use the network tab to see that 6 networks requests are made without ReactFire but only 5 are made with using it, you can also see the breakdown of chunks in the lighthouse analysis.

Lighthouse Treemap without ReactFire Lighthouse Treemap with ReactFire

This is quite a big blocker for me so any help would be appreciated.

Steps to reproduce

Open the two codesandbox's/netlify deployments above.

Expected behavior

Firestore is loaded in a separate chunk.

Actual behavior

Firestore is loaded in the initial chunk.

jhuleatt commented 2 years ago

Hi @Mattinton, this is really interesting, thanks for the repros.

My first thought was that maybe we were accidentally only exporting the .cjs build of ReactFire, but we do have the module field in our package.json, so I don't think that's it. Will keep investigating.

jhuleatt commented 2 years ago

I wonder if it's because our package.json is missing the exports field. react-scripts, which is bundling your examples, uses webpack, and it looks like webpack expects the exports field. I'm not sure if it looks at the module field. The webpack docs say:

exports field is preferred over other package entry fields like main, module, browser or custom ones.

Related TSDX issue (ReactFire uses TSDX for the build): https://github.com/jaredpalmer/tsdx/issues/298#issuecomment-786771416


update: I pulled https://codesandbox.io/s/elegant-roentgen-2iyt8?file=/src/App.tsx down locally, npm installed, then modified ReactFire's package.json in the node_modules folder to have an exports field. Then built and checked the bundle. Doesn't seem to have made a difference.

jhuleatt commented 2 years ago

It seems that just using FirebaseAppProvider (without any call at all to import or lazy import Firestore) is enough to pull Firestore and Storage into the bundle 😬 🤯

I'm not sure why, because FirebaseAppProvider doesn't import either of those. It only imports the following:

https://github.com/FirebaseExtended/reactfire/blob/f768f4d3c3be4ab5a3611143b8ceda19ede1dc95/src/firebaseApp.tsx#L1-L2

When I try to just import getApps, initializeApp, registerVersion directly in an app (without importing FirebaseAppProvider), Firestore and Storage are NOT included in the build, so it doesn't appear to be a bug in the Firebase JS SDK itself.

I think it has something to do with our build.


UPDATE: After investigating more, I didn't have tree shaking properly enabled in my tests for my comment above. Once I got tree shaking set up, the extra stuff was dropped from my bundle

matt-kinton commented 2 years ago

I did fork the repo and play around with it myself and also had no luck. I did notice that importing performance lazily didn't add to the overall package size and worked as expected. What are the differences between performance and firestore/storage?

matt-kinton commented 2 years ago

Hey @jhuleatt did you ever make any progress on this? Cheers

alexduhem commented 2 years ago

Hey there, got the same issue, I just pulled all my hairs out my head to know what libraries was causing this.

finally I just did a little hack with the webpack config by using https://webpack.js.org/configuration/externals/

config.externals = { 'firebase/database': 'root Math', };

this hack force remove the firebase/database dependency out of the bundle. why putting Math ? because webpack will add a small line in the bundle : "e.exports=math", so I used a builtin package to export to not crash the whole app

Gbuomprisco commented 1 year ago

When using Next.js, every module in Firebase appears to be bundled: https://github.com/FirebaseExtended/reactfire/issues/489#issuecomment-1249173518