Unable to prevent Navigation to Next page React Firebase

Viewed 70

I am creating a web app using firebase and react. My signup function works but I am having some trouble with edge cases such as when a user tries to signup with an email that already exists. How do I prevent navigation to the next page?

async function handleSignup(e) {
        e.preventDefault()
        validatePassword()
        try {
            // setError('')
            setLoading(true)
            await signup(emailRef.current.value, passwordRef.current.value)
            history.push('/profile')
            console.log('created User' + currentUser.uid)
        } catch {
            setError('Failed to Create an Account')
        }
        setLoading(false)
    }
function signup(email, password) {
        auth.createUserWithEmailAndPassword(email, password)
            .then((cred) => {
                return db.collection('students').doc(cred.user.uid).set()
            })
            .catch((e) => {
                if (e.code === 'auth/email-already-in-use') {
                    console.log('Email already in Use')
                    alert('Email already in use')
                }
                if (e.code === 'auth/weak-password')
                    alert(
                        'Password must be at least 7 characters long and contain special characters'
                    )
            })
    }
4 Answers

You need to refactor your async singup using Promise.

const signup = (email, password) => new Promise((resolve, reject) => {
  auth.createUserWithEmailAndPassword(email, password)
      .then((cred) => {
          return resolve(db.collection('students').doc(cred.user.uid).set())
      })
      .catch((e) => {
          if (e.code === 'auth/email-already-in-use') {
              console.log('Email already in Use')
              return reject('Email already in use')
          }
          if (e.code === 'auth/weak-password')
              return reject('Password must be at least 7 characters long and contain special characters')
      })
});

// and you can use now the try ... catch

try {
  // setError('')
  setLoading(true)
  await signup(emailRef.current.value, passwordRef.current.value)
  history.push('/profile')
  console.log('created User' + currentUser.uid)
} catch(error) {
  setError(error)
}

An already existing email is not an edge case :)

history.push('/profile') should only be invoked if sign-up is succesfull, so if signup fails it should throw an error and go to the catch block. might be worth to check if signup function throws an error as you expect

One way of doing so is when you get an error in the signup function, you should throw the error so handleSignup won't execute the code that comes after await signup(...).

Basically, your goal is to break out of the "success block" in case there's an error. alert() will not provide the expected behavior simply because it will not throw anything and that is the reason history.push triggers even though your signup rejected and the alert showed up.

Instead, it will catch the error and execute setError(...).

Therefore, your signup function would look something like this,

function signup(email, password) {
  auth.createUserWithEmailAndPassword(email, password)
    .then((cred) => {
      return db.collection('students').doc(cred.user.uid).set()
    })
    .catch((e) => {
      if (e.code === 'auth/email-already-in-use') {
        throw new Error(`Email is already in use, ${e}`)
      }
      if (e.code === 'auth/weak-password')
        throw new Error('Password must be at least 7 characters long and contain special characters')
      }
    )}

Firebase is not allowing you to signup up twice with the same email. I recommend using their error messages. They also do not allow entering a password under six characters.

Are you looking for a specific solution or a general solution that will fit other systems?.

You can try my repository. I worked with the (free) Firebase Auth service and the errors they send back.

Related