Can't Lazy Load Firebase v9's "useAuthState" & "auth" in React - reactjs

I have a React app that I'm trying to optimise. It's easy enough to Lazy Load components, but where I've found myself stuck is trying to asynchronously load and code-split the firebase dependencies.
They take up 80% of the initial loading js, and I don't need them to do anything until the page loads anyway, but they are bundled with the main.chunk.js.
I've tried to Lazy import:
import { useAuthState } from "react-firebase-hooks/auth";
import { auth } from "./firebase";
function App() {
const [user] = useAuthState(auth);
...
But you're not allowed to import Non-ReactComponent functions.
So I tried to load the useAuthState() in useEffect, but i'm not allowed to set hooks to a hook, which kind of makes sense.
This is the error I get:
React Hook "useAuthState" cannot be called inside a callback.
How can I Lazy Load the firebase components till after the page is initially loaded?
This is what the main.js is compiled with:
…js/main.bbdadee4.js(herospace.app)
../node_modules/#firebase/firestore/dist/index.esm2017.js
../node_modules/react-dom/cjs/react-dom.production.min.js
../node_modules/#firebase/firestore-compat/src/api/database.ts
../node_modules/#firebase/auth/src/core/auth/auth_impl.ts
../node_modules/#firebase/webchannel-wrapper/node_modules/google-closure-library/closure/goog/labs/net/webchannel/webchannelbase.js
But
../node_modules/react-dom/cjs/react-dom.production.min.js
is the only one I need to load the initial state of the page, which is why i'm trying to delay any firebase scripts from being loaded.
Any help is greatly appreciated, thank you!

I found a work-around, that doesn't bundle the firebase packages, or lazy load them, but instead I just extended a route for the authorized part of the app.
Instead of example.com/a-blog-post being on the same route as example.com/editProfile (a route that needs Firebase), I've moved everything to example.com/app/editProfile
I then only load Firebase imports in /app, and Lazy Load the component that renders when /app is reached.
I would still enjoy a lazy load option for firebase, but until then, this solution will do fine. Both mobile & desktop PageSpeed scores are green & 95+/100, so I'm satisfied.
I'm not marking this as the answer, because I still think there's a more proper & valid answer.

Related

Tree shaking from remote import

This question might seem unusual and a little anti-pattern but at this stage I am just trying to figure out what is possible.
The situation is that I have a few components which are available from a remote import (via Webpack 5's Module Federation). The caveat is that I don't want to lazily load them.
Once imported, the components are passed into a HOC to enrich some functionality and then exported:
import ComponentFooRemote from 'testRemote/Foo'
import ComponentBarRemote from 'testRemote/Bar'
const ComponentFoo = withEnrichedFunctionality(ComponentFooRemote)
const ComponentBar = withEnrichedFunctionality(ComponentBarRemote)
export {
ComponentFoo,
ComponentBar,
}
Functionally this works as expected. The components can be imported, rendered, and no components are loaded twice.
The issue is that the code imported from the remotes are always loaded. If I don't use ComponentBar - or even if I delete ComponentBar - the code from the remote will be downloaded as long as the original import is present. This is happening when using both development and production mode in the Webpack config.
Does anyone know if I can tree-shake these imports or restructure the code to better optimise the performance? Ideally I would like to import the components from the same path.

Tweaking react lazy loading webpack output to combine two webapps

We have a React Webapp where every route is lazy loaded:
const HomeScreen = React.lazy(() => import('../components/HomeScreen/HomeScreen'));
This, as expected, generates various JS files during a production build:
main.js
1.main.js
2.main.js
Is there any way to load those generated production code in another Webapp we have?
Do they simply replace the content of a specific DOM element? and if so, how do they go about it?
You can use Module Fedration for this
https://levelup.gitconnected.com/microfrontends-module-federation-exposing-remote-from-legacy-react-webpack4-created-by-f3753d70d34e

Where should I put general app config in a Gatsby project?

I'm new to Gatsby, but familiar with CRA. I'm using the default Gatsby setup generated by running gatsby new <project_name> from Gatsby CLI.
I have some general config that I want run in my whole project, regardless of current page - for example:
import { enableMapSet, enableES5 } from "immer";
enableMapSet();
enableES5();
In CRA projects I put this stuff inside App.tsx, but can't figure out what the right place is in a Gatsby project.
In Gatsby, you don't have a single configuration file per se. There are several APIs exposed by Gatsby that allows you to configure one specific part of your site.
To run a set of functions regardless of the page you can use a gatsby-browser.js (placed in the root of the project).
Basically, Gatsby exposes a bunch of APIs that lets you respond to actions within the browser, and wrap your site in additional components. The Gatsby Browser API gives you many options for interacting with the client-side of Gatsby. One of them, onClientEntry fits your requirements. From its documentation:
onClientEntry
(_: emptyArg, pluginOptions: pluginOptions) => undefined
Called when the Gatsby browser runtime first starts.
Applied to your code, your gatsby-browser.js should look like:
import { enableMapSet, enableES5 } from "immer";
import React from 'react';
export const onClientEntry = () =>{
enableMapSet();
enableES5();
};
The snippet above will trigger enableMapSet() and enableES5() on every page.

React lazy loading javascript file

I am trying to make my application more performant with React.lazy. As the Ethereum lightwallet is a huge file, I would like to put it into a separate bundle. The currently working import is the following:
import lightwallet from 'eth-lightwallet/dist/lightwallet.min.js';
When I try to import using the lazy syntax
const lightwallet = React.lazy(() => import('eth-lightwallet/dist/lightwallet.min.js'));
Only a React.lazy object ($$typeof: Symbol(react.lazy)) is returned instead of the lightwallet object. I think this is due to the fact that lightwallet doesn't have a default export. How could I get around this problem? Thanks!
I suggest following the example here:
https://reacttraining.com/react-router/web/guides/code-splitting
react-loadable is an npm package that makes code-splitting (a.k.a lazy loading) quite easy and also provides you the ability to render a loading component until the lazy load has finished.
The only gotcha is that if you're using Babel to transpile your code bundles, you'll have to add support for the Dynamic Import syntax, webpack already has this by default, but Babel doesn't.
The Babel Plugin:
#babel/plugin-syntax-dynamic-import
will make this possible by adding support for the syntax.
I recommend react-loadable over React.lazy as it makes displaying a loading component while the lazy-load happens VERY easy, and provides you hooks to display an error component and retry the import in the case that it fails.
Read more about react-loadable here:
https://github.com/jamiebuilds/react-loadable
Your code would look something like this:
import Loadable from 'react-loadable';
import Loading from './YOUR-LOADING-COMPONENT';
const LoadableWallet = Loadable({
loader: () => import('eth-lightwallet/dist/lightwallet.min.js'),
loading: Loading,
});
export default class Wallet extends React.Component {
render() {
return <LoadableWallet/>;
}
}
Make sure that your react version is up to date in React v16.6.0. And also the core idea behind the React. lazy is React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. But is this scenario min.js won't give any promise. Most probably That didn't work.

ReactJS: Load .less only when component is used

I am probably missing a verry basic understanding of loading less files in ReactJS, but I am unable to solve the following issue.
I have created components and created less files for each of them, for example:
import * as React from 'react';
import Row from 'antd/lib/row';
import Col from 'antd/lib/col';
import { NavLink } from 'react-router-dom';
import '../styles/how-it-works-styles.less';
import Icon from 'antd/lib/icon';
class HowItWorksComponent extends React.PureComponent<Props> {
public constructor(props: Props) {
super(props);
}
public render() {
return (
<Row className={'steps-row'}>
Now when I load a page where this component is not at all used, the less file is stilling being loaded into the dom. Could someone explain why this is happening and how i can prevent this from happening?
Your problem is not really about less, but a general problem of how to bundle a web app optimally. Since you're not providing your main app component or your webpack config (assuming you're using webpack for bundling), I obviously don't know the details of your setup. In general however, the standard configuration is to bundle all the components and other imports reachable from the entry point file into one big file. The fact that you use react router or similar to split your app into "pages" doesn't change this, as react router only affects which components are rendered when, not the bundling.
If you need to split your app into multiple bundles (which IMO requires a relatively large app to consider) you can use dynamic imports to make some of your components "Loadable". This means they will be placed in a separate bundle which is only loaded as needed. There is a tutorial in the react router documentation on how to set this up: https://reacttraining.com/react-router/web/guides/code-splitting

Resources