0

There is a form that the user fills up with this information like email and password, than he clicks on the submit button to start a payment process. But before i let the person proceed, i need to check if the user already has an email with the same email address already in the database. When i do setRetrievedEmailAddress and set the email address that i retrieve(if it exists) to the variable retrievedEmailAddress, it does update immediately, but the second time and onwards when i click on the button that the state is changed, as can be seen in the screen shot below. I cant use useEffect hook inside an event handler, so that is out of the question. I need the state to be updated immedietly because otherwise the person will go ahead with the payment process and end up paying twice because he already has an account with us. So my question is, how do i get the state to be updated immedietly?

enter image description here

import React, { useState, useEffect } from 'react';
import { Link } from '@reach/router';
import { auth, database, firestore } from '../firebase';
import { generateUserDocument } from '../firebase';
import { loadStripe } from '@stripe/stripe-js';

import logo from '../images/guide_logo.png';
import './SignUp.css';
import './InputFieldStyles.css';
import { string } from 'prop-types';
import PaymentConfirmation from './PaymentConfirmation.jsx';

import "firebase/auth";
import "firebase/firestore";
import firebase from "firebase/app";
import { createImportSpecifier } from 'typescript';
import { isCompositeComponentWithType } from 'react-dom/test-utils';

const SignUp = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [passwordre, setPasswordre] = useState('');
  const [displayName, setDisplayName] = useState('');
  const [error, setError] = useState(null);
  const [retrievedEmailAddress, setRetrievedEmailAddress] = useState(null);
  
  const StartPaymentProcess = async (event) => {
    event.preventDefault();
        
    database.collection("users").where("email", "==", "nonpaidmembership@gmail.com")
      .get()
      .then((querySnapshot) => {
        if (querySnapshot.exists) {}
          querySnapshot.forEach((doc) => {
            if (doc.exists) {
              setRetrievedEmailAddress(doc.data().email);
              console.log(doc.id, " => ", doc.data().email);
            }
            else {
              console.log("doc doesnt exist");
            }
            
          });
      })
      .catch((error) => {
          console.log("Error getting documents: ", error);
      }); 


    console.log(retrievedEmailAddress);

    //This means that there user already has an account, dont move ahead and 
    //give error message
    if (retrievedEmailAddress != null) {
      setError('User Already exists, please choose a different email');
      console.log("user already exists");
    }
    else {
    //Start payment Process
    }
  };

  const onChangeHandler = (event) => {
    const { name, value } = event.currentTarget;

    if (name === 'userEmail') {
      setEmail(value);
      setDisplayName(value);
    } else if (name === 'userPassword') {
      setPassword(value);
    } else if (name === 'userPasswordre') {
      setPasswordre(value);
      if (event.target.value !== password) {
        setError('Passwords do not match');
      } else {
        setError('');
      }
    }
  };

  const setSessionStorageValues = (event) => {
    sessionStorage.setItem('email',email);
    sessionStorage.setItem('password',password);
  }

  onkeypress = (event) => {
    if (event.which === 13) {
      StartPaymentProcess(event);
    }
  }

  return (
    <div
      className="signup-container"
      style={{
        display: 'flex',
        width: '100%',
        height: '100%',
        backgroundColor: 'white',
      }}
    >
      <div
        className="left-side"
        style={{
          backgroundImage:
            'linear-gradient(to bottom left, #4A2C60, rgb(51,54,155,.75))',
          flex: '1',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <img src={logo} alt="Logo" style={{ opacity: '0.15' }} />
      </div>

      <div
        className="right-side"
        style={{
          backgroundColor: 'white',
          display: 'flex',
          flexDirection: 'column',
          flex: '1',
          textAlign: 'center',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <div
          style={{
            flex: '3',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'space-around',
          }}
        >
          <div className="title" style={{ justifyContent: 'space-around' }}>
            <p
              style={{
                fontSize: '8vh',
                color: '#2E3191',
                marginBottom: '20px',
              }}
            >
              Sign Up
            </p>
            <p style={{ fontSize: '3vh', color: 'gray' }}>
              Please Enter Your Sign-up Information
            </p>
          </div>
        </div>
        <div
          className="form-group"
          style={{
            width: '50%',
            display: 'flex',
            flexDirection: 'column',
            flex: 5,
          }}
        >
          <form onSubmit={(event) => {
            StartPaymentProcess(event); 
            setSessionStorageValues(event);
            }}>
            <div style={{display:'inline-block'}}>
              <div id="emailHeading">
                Email
              </div>

              <input
                type="email"
                name="userEmail"
                value={email}
                placeholder="Email"
                id="userEmail"
                onChange={(event) => onChangeHandler(event)}
              />
            
              <div id="passwordHeading">
                  Password
              </div>

              <input
                type="password"
                name="userPassword"
                value={password}
                placeholder="Password"
                id="userPassword"
                onChange={(event) => onChangeHandler(event)}
              />

              </div>

              <input
                type="password"
                name="userPasswordre"
                value={passwordre}
                placeholder="Password (Re-Enter)"
                id="userPasswordre"
                onChange={(event) => onChangeHandler(event)}
              />

              <div style={{ color: 'red', fontSize: 'small' }}>{error}</div>

              <div style={{ height: '30px' }} />
              <button className="sign-up-button" type="submit">
                Signup and Checkout
              </button>

            </form>

          <div className="text-above-signup_button">Already have an account?</div>

          <Link to="/" className="go-to-signin-page-button">Sign in here</Link>
        </div>
      </div>
    </div>
  );
};
export default SignUp;
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Q-man
  • 95
  • 2
  • 12
  • 1
    `setState` is asynchronous. There is no synchronous alternative, you *have* to use `useEffect`. – BenM Mar 03 '21 at 19:52
  • i cant use the useEffect hook inside an event handler, it gives an error – Q-man Mar 03 '21 at 19:53
  • You have an X-Y problem, you need to disable the form submission while you check if the user already exists. – BenM Mar 03 '21 at 19:54
  • interesting idea, but how would i go about doing that? even if i get data fromfirebase inside of 'onChangeHandler' i am still stuck on the same problem, cant use useEffect in there as well, i dont know where else to put the code to check if the user already has an account with us or not? – Q-man Mar 03 '21 at 19:56
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Adam Jenkins Mar 04 '21 at 00:07

4 Answers4

1

This happens because you are calling an async call so when you call you console.log(retrievedEmailAddress) the information has not arrived yet. Therefore the best approach for you requirement is make the validation inside the answer:

  database.collection("users").where("email", "==", "nonpaidmembership@gmail.com")
      .get()
      .then((querySnapshot) => {
        if (querySnapshot.exists) {}
          querySnapshot.forEach((doc) => {
            if (doc.exists) {
              setRetrievedEmailAddress(doc.data().email);
              console.log(doc.id, " => ", doc.data().email);
              //here is safe to validate your data
              if (doc.data().email != null) {
                     setError('User Already exists, please choose a different email');
                     console.log("user already exists");
              }
              else {
                     //Start payment Process
              }
            }
            else {
              console.log("doc doesnt exist");
            }
            
          });
      })
      .catch((error) => {
          console.log("Error getting documents: ", error);
      });  
      console.log(retrievedEmailAddress); // here the information will no be present the first time
vi calderon
  • 196
  • 1
  • 8
0

so, your issue is that setRetrievedEmailAddress is taking some time to update retrievedEmailAddress

Try this...

setRetrievedEmailAddress(prevState => (doc.data().email));
0

setState is async you don't get its updated value in the function you are updating it, create a state verified;

  const [verified, setVerified] = useState(false);

use useEffect hook as mentioned below

useEffect(() => {
 if(verified){
 // proceed to payment
 // ....

 // at the end don't forget to setVerified(false)


  }
 }, [verified])
Arfan ali
  • 429
  • 2
  • 5
  • Cant use useEffect hook inside of an event handler, i believe it is against the rules of hooks – Q-man Mar 03 '21 at 20:09
  • you don't need to use it in an event handler – Arfan ali Mar 03 '21 at 20:13
  • just add it after this line ... const [retrievedEmailAddress, setRetrievedEmailAddress] = useState(null); – Arfan ali Mar 03 '21 at 20:16
  • let me know if you still facing any issue – Arfan ali Mar 03 '21 at 20:17
  • if i do useEffect(() => { if (retrievedEmailAddress != null) { console.log("user already exists"); setError('User Already exists, please choose a different email'); } else { console.log("user DOES NOT EXIST") StartPaymentProcess(); } }) and i wrote this just aboce the startPaymentProcess event handler, i get an error – Q-man Mar 03 '21 at 20:43
  • you cant call StartPaymentProcess there because you are setting retrievedEmailAddress in it that hits the useEffect method. – Arfan ali Mar 03 '21 at 20:56
  • create a new function that only contains further payment process and call it in the else statement of eseEffect – Arfan ali Mar 03 '21 at 20:58
  • ok i undersatnd, the problem is that the payment process will run whenever i enter a valid email and password, because useeffect runs whenver you enter something in the field, but i want it to run WHEN the person clicks the start payment button – Q-man Mar 03 '21 at 23:00
0

you are using async function... so uh should put await keyword before request

await database.collection("users").where("email", "==", "nonpaidmembership@gmail.com")
  .get()

Also you are not setting retrievedEmailAddress to null in any case... which will be problematic for the scenario

  • user input email id that already exist (email stored in retrievedEmailAddress) then
  • user input email id that doesnot exist (but retrievedEmailAddress not updated so if (retrievedEmailAddress != null) will be true and payment process won't start)
  • I did add await just now, didnt solve the problem, on the first try i still get null – Q-man Mar 03 '21 at 20:31
  • you have to apply both of my answers the above one and setRetrievedEmailAddress is taking some time to update retrievedEmailAddress Try this... setRetrievedEmailAddress(prevState => (doc.data().email)); – Muhammad Bilal Mar 03 '21 at 20:48
  • I did apply both of your suggestions, still nothing – Q-man Mar 03 '21 at 20:50