Open gvillenave opened 2 years ago
Hi, is it ready to be merged?
When is it getting merged?
@jhuleatt can you review and merge?
Hi all. I need this PR merged right now. Thank you
Hey when is this fix getting merged?
@jhuleatt any feedback on the PR? Can we get this merged soon?
waiting for merge
waiting for merge +1
waiting for merge +2
I too would greatly appreciate this getting merged
Based on this PR, if anyone is using React (create-react-app and not Next.js or other) and is looking for a way of solving this issue, you can get rid of firebaseui-web-react
and just use firebaseui
.
These are the steps to do that:
react-firebaseui
(yarn remove react-firebaseui
)firebaseui
(yarn add firebaseui
)StyledFirebaseAuth.tsx
import { useEffect, useRef, useState } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import * as firebaseui from 'firebaseui';
import 'firebaseui/dist/firebaseui.css';
interface Props { // The Firebase UI Web UI Config object. // See: https://github.com/firebase/firebaseui-web#configuration uiConfig: firebaseui.auth.Config; // Callback that will be passed the FirebaseUi instance before it is // started. This allows access to certain configuration options such as // disableAutoSignIn(). uiCallback?(ui: firebaseui.auth.AuthUI): void; // The Firebase App auth instance to use. firebaseAuth: any; // As firebaseui-web className?: string; }
const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => { const [userSignedIn, setUserSignedIn] = useState(false); const elementRef = useRef(null);
useEffect(() => {
// Get or Create a firebaseUI instance.
const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth);
if (uiConfig.signInFlow === 'popup')
firebaseUiWidget.reset();
// We track the auth state to reset firebaseUi if the user signs out.
const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, (user) => {
if (!user && userSignedIn)
firebaseUiWidget.reset();
setUserSignedIn(!!user);
});
// Trigger the callback if any was set.
if (uiCallback)
uiCallback(firebaseUiWidget);
// Render the firebaseUi Widget.
// @ts-ignore
firebaseUiWidget.start(elementRef.current, uiConfig);
return () => {
unregisterAuthObserver();
firebaseUiWidget.reset();
};
}, [firebaseui, uiConfig]);
return <div className={className} ref={elementRef} />;
};
export default StyledFirebaseAuth;
That's it! You can use this component without the need for this library.
If you need `FirebaseAuth` instead of the `StyledFirebaseAuth`, you can just remove the `import 'firebaseui/dist/firebaseui.css';` line from the component and you'll be good to go!
I just did this for my personal project and reduced the main bundle size by ~9KB.
waiting for merge +3
me too!
Please merge it :)
merge 🙏
Pretty please with chocolate sprinkles on top? 🙏
Give me permission and I'll merge it. This project feels unmaintained.
waiting for merge +999
@jhuleatt
Can this be merged?
Please merge this; no reason not to.
Let's go! @jhuleatt Would be really nice if you would find the time, thanks!
@jhuleatt If you can't find the time is it possible to dispatch the review to a different maintainer of this project? I think a lot of people would love to see this PR being merged. Thanks for your time!
Has anyone found a way of solving this using Next.js?
Please merge 🙏
@jhuleatt and @Skyblueballykid , this merge is important for my team's project! Please be a kind soul and help us unblock. At least, by when should this PR get merged in?
For anyone looking for a solution for Next.js
, you can use the following code and completely get rid of this library:
react-firebaseui
(yarn remove react-firebaseui
)firebaseui
(yarn add firebaseui
)StyledFirebaseAuth.tsx
import { useEffect, useRef, useState } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import 'firebaseui/dist/firebaseui.css';
import {auth} from "firebaseui";
interface Props {
// The Firebase UI Web UI Config object.
// See: https://github.com/firebase/firebaseui-web#configuration
uiConfig: auth.Config;
// Callback that will be passed the FirebaseUi instance before it is
// started. This allows access to certain configuration options such as
// disableAutoSignIn().
uiCallback?(ui: auth.AuthUI): void;
// The Firebase App auth instance to use.
firebaseAuth: any; // As firebaseui-web
className?: string;
}
const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => {
const [firebaseui, setFirebaseui] = useState<typeof import('firebaseui') | null>(null);
const [userSignedIn, setUserSignedIn] = useState(false);
const elementRef = useRef(null);
useEffect(() => {
// Firebase UI only works on the Client. So we're loading the package only after
// the component has mounted, so that this works when doing server-side rendering.
setFirebaseui(require('firebaseui'));
}, []);
useEffect(() => {
if (firebaseui === null )
return;
// Get or Create a firebaseUI instance.
const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth);
if (uiConfig.signInFlow === 'popup')
firebaseUiWidget.reset();
// We track the auth state to reset firebaseUi if the user signs out.
const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, user => {
if (!user && userSignedIn)
firebaseUiWidget.reset();
setUserSignedIn(!!user);
});
// Trigger the callback if any was set.
if (uiCallback)
uiCallback(firebaseUiWidget);
// Render the firebaseUi Widget.
// @ts-ignore
firebaseUiWidget.start(elementRef.current, uiConfig);
return () => {
unregisterAuthObserver();
firebaseUiWidget.reset();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [firebaseui, uiConfig]);
return <div className={className} ref={elementRef} />;
};
export default StyledFirebaseAuth;
That's it! You can use this component without the need for this library.
If you need FirebaseAuth
instead of the StyledFirebaseAuth
, you can just remove the import 'firebaseui/dist/firebaseui.css';
line from the component and you'll be good to go!
Waiting for merge 🙏
pls merge!
React solution
Based on this PR, if anyone is using React (create-react-app and not Next.js or other) and is looking for a way of solving this issue, you can get rid of
firebaseui-web-react
and just usefirebaseui
. These are the steps to do that:
- Remove
react-firebaseui
(yarn remove react-firebaseui
)- Add
firebaseui
(yarn add firebaseui
)- Copy the below code as
StyledFirebaseAuth.tsx
import { useEffect, useRef, useState } from 'react'; import { onAuthStateChanged } from 'firebase/auth'; import * as firebaseui from 'firebaseui'; import 'firebaseui/dist/firebaseui.css'; interface Props { // The Firebase UI Web UI Config object. // See: https://github.com/firebase/firebaseui-web#configuration uiConfig: firebaseui.auth.Config; // Callback that will be passed the FirebaseUi instance before it is // started. This allows access to certain configuration options such as // disableAutoSignIn(). uiCallback?(ui: firebaseui.auth.AuthUI): void; // The Firebase App auth instance to use. firebaseAuth: any; // As firebaseui-web className?: string; } const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => { const [userSignedIn, setUserSignedIn] = useState(false); const elementRef = useRef(null); useEffect(() => { // Get or Create a firebaseUI instance. const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth); if (uiConfig.signInFlow === 'popup') firebaseUiWidget.reset(); // We track the auth state to reset firebaseUi if the user signs out. const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, (user) => { if (!user && userSignedIn) firebaseUiWidget.reset(); setUserSignedIn(!!user); }); // Trigger the callback if any was set. if (uiCallback) uiCallback(firebaseUiWidget); // Render the firebaseUi Widget. // @ts-ignore firebaseUiWidget.start(elementRef.current, uiConfig); return () => { unregisterAuthObserver(); firebaseUiWidget.reset(); }; }, [firebaseui, uiConfig]); return <div className={className} ref={elementRef} />; }; export default StyledFirebaseAuth;
That's it! You can use this component without the need for this library. If you need
FirebaseAuth
instead of theStyledFirebaseAuth
, you can just remove theimport 'firebaseui/dist/firebaseui.css';
line from the component and you'll be good to go!I just did this for my personal project and reduced the main bundle size by ~9KB.
Perfect and worked like a charm! Thanks.
Hey guys come on! Many people are looking for this, it is as simple as merging it, just do it or give access to someone else that will actually maintain it.
Next.js solution
For anyone looking for a solution for
Next.js
, you can use the following code and completely get rid of this library:
- Remove
react-firebaseui
(yarn remove react-firebaseui
)- Add
firebaseui
(yarn add firebaseui
)- Copy the below code as
StyledFirebaseAuth.tsx
import { useEffect, useRef, useState } from 'react'; import { onAuthStateChanged } from 'firebase/auth'; import 'firebaseui/dist/firebaseui.css'; import {auth} from "firebaseui"; interface Props { // The Firebase UI Web UI Config object. // See: https://github.com/firebase/firebaseui-web#configuration uiConfig: auth.Config; // Callback that will be passed the FirebaseUi instance before it is // started. This allows access to certain configuration options such as // disableAutoSignIn(). uiCallback?(ui: auth.AuthUI): void; // The Firebase App auth instance to use. firebaseAuth: any; // As firebaseui-web className?: string; } const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => { const [firebaseui, setFirebaseui] = useState<typeof import('firebaseui') | null>(null); const [userSignedIn, setUserSignedIn] = useState(false); const elementRef = useRef(null); useEffect(() => { // Firebase UI only works on the Client. So we're loading the package only after // the component has mounted, so that this works when doing server-side rendering. setFirebaseui(require('firebaseui')); }, []); useEffect(() => { if (firebaseui === null ) return; // Get or Create a firebaseUI instance. const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth); if (uiConfig.signInFlow === 'popup') firebaseUiWidget.reset(); // We track the auth state to reset firebaseUi if the user signs out. const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, user => { if (!user && userSignedIn) firebaseUiWidget.reset(); setUserSignedIn(!!user); }); // Trigger the callback if any was set. if (uiCallback) uiCallback(firebaseUiWidget); // Render the firebaseUi Widget. // @ts-ignore firebaseUiWidget.start(elementRef.current, uiConfig); return () => { unregisterAuthObserver(); firebaseUiWidget.reset(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [firebaseui, uiConfig]); return <div className={className} ref={elementRef} />; }; export default StyledFirebaseAuth;
That's it! You can use this component without the need for this library. If you need
FirebaseAuth
instead of theStyledFirebaseAuth
, you can just remove theimport 'firebaseui/dist/firebaseui.css';
line from the component and you'll be good to go!
worked like a charm! Thank you 🤙
This is fantastic, but I found that redirects (e.g. with Google SSO or Email Link) still do not work correctly in development due to React.StrictMode and the new React 18 "strict effects" feature forcing an unmount and remount after firebaseUiWidget.start(...) transitions from temporary redirect link to the sign-in URL. Couldn't figure out a clean solution here, so went with a hack that skips unmount/remount when redirection is in play.
const skipStrictEffects = useRef(false);
useEffect(() => {
...
if (!skipStrictEffects.current) {
skipStrictEffects.current = firebaseUiWidget.isPendingRedirect();
firebaseUiWidget.start(elementRef.current, uiConfig);
}
return () => {
if (!skipStrictEffects.current) {
unregisterAuthObserver();
firebaseUiWidget.reset();
}
};
}, [firebaseui, uiConfig]);
can we get this merged please?
@gvillenave Is this ready to be merged?
Could we merge this please @jhuleatt?
Could we merge this please @jhuleatt?
I think this project is dead - 11 month since last update with many pleas to merge. I recommend removing the dependency as mentioned above.
Since my project isn't using TSX and I had issues converting from TSX to JSX/JS, I ended up doing the following:
yarn remove react-firebaseui
yarn add https://github.com/gvillenave/firebaseui-web-react.git#acb47b46dc39682d13f2b117524bda95ec1aeddf
In my project, I am importing as follows:
import StyledFirebaseAuth from 'react-firebaseui/dist/StyledFirebaseAuth';
Not ideal, I know 👎
It is a shame that this project is dead
Next.js solution
For anyone looking for a solution for
Next.js
, you can use the following code and completely get rid of this library:
- Remove
react-firebaseui
(yarn remove react-firebaseui
)- Add
firebaseui
(yarn add firebaseui
)- Copy the below code as
StyledFirebaseAuth.tsx
import { useEffect, useRef, useState } from 'react'; import { onAuthStateChanged } from 'firebase/auth'; import 'firebaseui/dist/firebaseui.css'; import {auth} from "firebaseui"; interface Props { // The Firebase UI Web UI Config object. // See: https://github.com/firebase/firebaseui-web#configuration uiConfig: auth.Config; // Callback that will be passed the FirebaseUi instance before it is // started. This allows access to certain configuration options such as // disableAutoSignIn(). uiCallback?(ui: auth.AuthUI): void; // The Firebase App auth instance to use. firebaseAuth: any; // As firebaseui-web className?: string; } const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => { const [firebaseui, setFirebaseui] = useState<typeof import('firebaseui') | null>(null); const [userSignedIn, setUserSignedIn] = useState(false); const elementRef = useRef(null); useEffect(() => { // Firebase UI only works on the Client. So we're loading the package only after // the component has mounted, so that this works when doing server-side rendering. setFirebaseui(require('firebaseui')); }, []); useEffect(() => { if (firebaseui === null ) return; // Get or Create a firebaseUI instance. const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth); if (uiConfig.signInFlow === 'popup') firebaseUiWidget.reset(); // We track the auth state to reset firebaseUi if the user signs out. const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, user => { if (!user && userSignedIn) firebaseUiWidget.reset(); setUserSignedIn(!!user); }); // Trigger the callback if any was set. if (uiCallback) uiCallback(firebaseUiWidget); // Render the firebaseUi Widget. // @ts-ignore firebaseUiWidget.start(elementRef.current, uiConfig); return () => { unregisterAuthObserver(); firebaseUiWidget.reset(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [firebaseui, uiConfig]); return <div className={className} ref={elementRef} />; }; export default StyledFirebaseAuth;
That's it! You can use this component without the need for this library. If you need
FirebaseAuth
instead of theStyledFirebaseAuth
, you can just remove theimport 'firebaseui/dist/firebaseui.css';
line from the component and you'll be good to go!
Have been looking into this for a bit and this seems to be by far the best fix atm. Turning off Strict Mode is very non-ideal, and I can confirm this works with the latest version of React while leaving it on. Works with Create-React-App (using Vite) as well. Thank you greatly!
A note for anyone experiencing an error like the following when using these solutions:
TypeError: Cannot read properties of undefined (reading 'EmailAuthProvider')
firebase/compat/auth
must be imported alongside firebase/compat/app
.
import firebase from "firebase/compat/app";
import "firebase/compat/auth";
This is fantastic, but I found that redirects (e.g. with Google SSO or Email Link) still do not work correctly in development due to React.StrictMode and the new React 18 "strict effects" feature forcing an unmount and remount after firebaseUiWidget.start(...) transitions from temporary redirect link to the sign-in URL. Couldn't figure out a clean solution here, so went with a hack that skips unmount/remount when redirection is in play.
const skipStrictEffects = useRef(false); useEffect(() => { ... if (!skipStrictEffects.current) { skipStrictEffects.current = firebaseUiWidget.isPendingRedirect(); firebaseUiWidget.start(elementRef.current, uiConfig); } return () => { if (!skipStrictEffects.current) { unregisterAuthObserver(); firebaseUiWidget.reset(); } }; }, [firebaseui, uiConfig]);
react 18 strict mode effects on useEffect is meant to tackle fast navigation or something like that. a better solution than above is to use event loop to run code that's affected by the unmount-remount, like so:
useEffect(() => {
if (!elementRef.current) return;
let firebaseUiWidget: auth.AuthUI;
let unregisterAuthObserver: Unsubscribe;
// firebase ui start in event loop to solve react 18 strict requirement.
const timeout = setTimeout(() => {
// Get or Create a firebaseUI instance.
firebaseUiWidget =
auth.AuthUI.getInstance() || new auth.AuthUI(firebaseAuth);
if (uiConfig.signInFlow === "popup") firebaseUiWidget.reset();
// We track the auth state to reset firebaseUi if the user signs out.
unregisterAuthObserver = onAuthStateChanged(firebaseAuth, (user) => {
if (!user && userSignedIn) firebaseUiWidget.reset();
setUserSignedIn(!!user);
});
// Trigger the callback if any was set.
if (uiCallback) uiCallback(firebaseUiWidget);
// Render the firebaseUi Widget.
firebaseUiWidget.start(elementRef.current as HTMLDivElement, uiConfig);
});
return () => {
clearTimeout(timeout);
if (unregisterAuthObserver) unregisterAuthObserver();
if (firebaseUiWidget) firebaseUiWidget.reset();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [uiConfig]);
Thank you for nice library and nice PR. It seems the change works on my service.
@jhuleatt I'm looking forward to your approval and merge.
componentWillMount
/componentWillUnmount
to auseEffect
hookuseRef
extract-text-webpack-plugin
(now deprecated) tomini-css-extract-plugin
andpostcss