Skip to content

Instantly share code, notes, and snippets.

@timc1
Created June 10, 2019 00:20
Show Gist options
  • Select an option

  • Save timc1/d559d0f8769b4badc0bfc22484fe97a3 to your computer and use it in GitHub Desktop.

Select an option

Save timc1/d559d0f8769b4badc0bfc22484fe97a3 to your computer and use it in GitHub Desktop.

Revisions

  1. timc1 created this gist Jun 10, 2019.
    211 changes: 211 additions & 0 deletions use-auth.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,211 @@
    import React from 'react'
    import firebaseConfig from '../path/to/firebase-config'
    import firebase from 'firebase/app'
    import 'firebase/auth'
    import FullPageLoading from '../path/to/full-page-loading'

    AuthProvider.actions = {
    setUser: 'SET_USER',
    toggleLoading: 'TOGGLE_LOADING',
    }

    const reducer = (
    state,
    action
    ) => {
    switch (action.type) {
    case AuthProvider.actions.setUser:
    return {
    user: action.payload.user,
    isInitiallyLoading: false,
    isLoading: false,
    }
    case AuthProvider.actions.toggleLoading:
    return {
    ...state,
    isLoading: action.payload.value,
    }
    default:
    throw new Error(`No case for type ${action.type} found.`)
    }
    }

    const AuthContext = React.createContext(undefined)

    export function AuthProvider({
    initialUser,
    children,
    }) {
    const [state, dispatch] = React.useReducer(reducer, {
    isInitiallyLoading: true,
    isLoading: false,
    user: null,
    })

    const signingInSoDontDispatchOnAuthStateChange = React.useRef(false)
    React.useEffect(() => {
    // Setup Firebase authentication state observer and get user data.
    if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig)
    }

    firebase.auth().onAuthStateChanged(function(user) {
    if (user) {
    // User is signed in.
    if (signingInSoDontDispatchOnAuthStateChange.current) {
    signingInSoDontDispatchOnAuthStateChange.current = false
    return
    }

    dispatch({
    type: AuthProvider.actions.setUser,
    payload: {
    user,
    },
    })
    } else {
    // User is signed out.
    dispatch({
    type: AuthProvider.actions.setUser,
    payload: {
    user: null,
    },
    })
    }
    })
    }, [])

    const signup = (email, password, displayName) => {
    signingInSoDontDispatchOnAuthStateChange.current = true

    toggleLoading(true)

    let user: any

    firebase
    .auth()
    .createUserWithEmailAndPassword(email, password)
    .then(() => {
    user = firebase.auth().currentUser
    user.sendEmailVerification()
    })
    .then(() => {
    user.updateProfile({
    displayName,
    })
    })
    .then(() => {
    toggleLoading(false)
    // Set user with displayName here because user.updateProfile
    // is async and our onAuthStateChanged listener will fire
    // before the user is updated. When that happens, user.displayName
    // value will be null.
    // Reference: https://github.com/firebase/firebaseui-web/issues/36
    const updatedUserWithDisplayName = {
    ...user,
    displayName,
    }

    dispatch({
    type: AuthProvider.actions.setUser,
    payload: {
    user: updatedUserWithDisplayName,
    },
    })
    })
    .catch(function(error) {
    // Handle Errors here.
    const errorCode = error.code
    const errorMessage = error.message

    console.log('errorCode', errorCode, 'errorMessage', errorMessage)
    toggleLoading(false)
    })
    }

    const signin = (email, password) => {
    toggleLoading(true)

    firebase
    .auth()
    .signInWithEmailAndPassword(email, password)
    .then(() => {
    toggleLoading(false)
    })
    .catch(function(error) {
    // Handle Errors here.
    const errorCode = error.code
    const errorMessage = error.message
    console.log('errorCode', errorCode, 'errorMessage', errorMessage)
    toggleLoading(false)
    })
    }

    const signout = () => {
    toggleLoading(true)

    firebase
    .auth()
    .signOut()
    .then(function() {
    // Sign-out successful.
    toggleLoading(false)
    })
    .catch(function(error) {
    // An error happened.
    toggleLoading(false)
    })
    }

    const sendResetPasswordEmail = (email) => {
    toggleLoading(true)

    firebase
    .auth()
    .sendPasswordResetEmail(email)
    .then(function() {
    // Email sent.
    toggleLoading(true)
    // TODO: Toggle success notification here.
    })
    .catch(function(error) {
    // An error happened.
    console.log('error', error)
    toggleLoading(false)
    })
    }

    const toggleLoading = (isLoading) => {
    dispatch({
    type: AuthProvider.actions.toggleLoading,
    payload: {
    value: isLoading,
    },
    })
    }

    const value = {
    user: initialUser || state.user,
    signup,
    signin,
    signout,
    sendResetPasswordEmail,
    isLoading: state.isLoading,
    }

    return state.isInitiallyLoading ? (
    <FullPageLoading />
    ) : (
    <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
    )
    }

    export default function useAuth() {
    const context = React.useContext(AuthContext)

    if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider')
    }

    return context
    }