Skip to content

Instantly share code, notes, and snippets.

@gragland
Last active August 27, 2022 15:16
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save gragland/25910a537e397844312870fcfc3ee74b to your computer and use it in GitHub Desktop.
Save gragland/25910a537e397844312870fcfc3ee74b to your computer and use it in GitHub Desktop.
React Hook recipe from https://usehooks.com
// Top level App component
import React from "react";
import { ProvideAuth } from "./use-auth.js";
function App(props) {
return (
<ProvideAuth>
{/*
Route components here, depending on how your app is structured.
If using Next.js this would be /pages/_app.js
*/}
</ProvideAuth>
);
}
// Any component that wants auth state
import React from "react";
import { useAuth } from "./use-auth.js";
function Navbar(props) {
// Get auth state and re-render anytime it changes
const auth = useAuth();
return (
<NavbarContainer>
<Logo />
<Menu>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
{auth.user ? (
<Fragment>
<Link to="/account">Account ({auth.user.email})</Link>
<Button onClick={() => auth.signout()}>Signout</Button>
</Fragment>
) : (
<Link to="/signin">Signin</Link>
)}
</Menu>
</NavbarContainer>
);
}
// Hook (use-auth.js)
import React, { useState, useEffect, useContext, createContext } from "react";
import * as firebase from "firebase/app";
import "firebase/auth";
// Add your Firebase credentials
firebase.initializeApp({
apiKey: "",
authDomain: "",
projectId: "",
appID: ""
});
const authContext = createContext();
// Provider component that wraps your app and makes auth object ...
// ... available to any child component that calls useAuth().
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
// Hook for child components to get the auth object ...
// ... and re-render when it changes.
export const useAuth = () => {
return useContext(authContext);
};
// Provider hook that creates auth object and handles state
function useProvideAuth() {
const [user, setUser] = useState(null);
// Wrap any Firebase methods we want to use making sure ...
// ... to save the user to state.
const signin = (email, password) => {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signup = (email, password) => {
return firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signout = () => {
return firebase
.auth()
.signOut()
.then(() => {
setUser(false);
});
};
const sendPasswordResetEmail = email => {
return firebase
.auth()
.sendPasswordResetEmail(email)
.then(() => {
return true;
});
};
const confirmPasswordReset = (code, password) => {
return firebase
.auth()
.confirmPasswordReset(code, password)
.then(() => {
return true;
});
};
// Subscribe to user on mount
// Because this sets state in the callback it will cause any ...
// ... component that utilizes this hook to re-render with the ...
// ... latest auth object.
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
// Cleanup subscription on unmount
return () => unsubscribe();
}, []);
// Return the user object and auth methods
return {
user,
signin,
signup,
signout,
sendPasswordResetEmail,
confirmPasswordReset
};
}
@gragland
Copy link
Author

gragland commented Jan 3, 2020

@waldothedeveloper the useAuth hook should always return an auth object, as the value is passed in ProvideAuth on line 62. I just tested with Next.js and I'm getting an auth object on server render, as well as client. Do you by chance have your code online somewhere I could check out?

@waldothedeveloper
Copy link

Hey, @gragland I do, check it out here:
link-to-project
Make sure you check the code from the sign_up_page branch NOT the master.

I am having a hard time to also try to protect the route:
/account on SSR with NextJS...any idea how to protect routes on Next JS?

@gragland
Copy link
Author

gragland commented Jan 4, 2020

@waldothedeveloper When you originally tested with useProvideAuth and useAuth did you include an _app.js file and wrap all pages with useProvideAuth? For Next.js you'll need to do that so that useAuth can read the context. For example:

import React from "react";
import App from "next/app";
import { ProvideAuth } from "./../util/auth.js";

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;

    return (
      <ProvideAuth>
          <Component {...pageProps} />
      </ProvideAuth>
    );
  }
}

export default MyApp;

@NavizDev
Copy link

add:
if (firebase.apps.length === 0) {
firebase.initializeApp(firebaseConfig);
}

@arx111
Copy link

arx111 commented Nov 2, 2020

should it be setUser(null) instead of setUser(false) ?

@surjithctly
Copy link

When using this snippet, I get the following error in Next.Js

Anyone know what does this mean or how to fix this? My Project is just a skeleton to implement this firebase auth. nothing else.

image

@surjithctly
Copy link

Nevermind, Its fixed.

I was not using Single Child inside <Link>. So adding <a> fixed it.

@dasveloper
Copy link

Do you need to include setUser(response.user); in all the signin, signup, etc. callbacks or could you just handle it in the onAuthStateChanged callback? It seems to me onAuthStateChanged is called for all of those events anyway?

@SimonChaumet
Copy link

It would be great to add a note for those who have an infinite loop to use useCallback on api functions : in my app I created an api loader and in the dependency list the linter always asks for my function in it, but when setting the user the function is recreated so is my loader and it creates an infinite loop.

@berbaroovez
Copy link

added typescript version
https://gist.github.com/berbaroovez/d6832dc280d2e0b1eca4f862bcf7df09
The User type is from firebase not sure what the import for that is

@lbustelo
Copy link

It would be great to add a note for those who have an infinite loop to use useCallback on api functions : in my app I created an api loader and in the dependency list the linter always asks for my function in it, but when setting the user the function is recreated so is my loader and it creates an infinite loop.

Should the useProvideAuth hook wrap all the provided functions in useCallback? If not… why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment