skip to Main Content

So basicall I wanted to create a registration process where you give your userName, email (but it can not exist in a database), password and then confirmed password. Project is in nextJS with pages router and typescript.

import React, { useState } from 'react'
import styles from './styles.module.css'
import { Header } from '@/components'
import Head from 'next/head'
import { hash } from '@/scripts/hashing'
import axios from 'axios'
import { getTokens } from '@/scripts/tokens'
import { store } from '@/redux/store'
import Link from 'next/link'
import { useDispatch } from 'react-redux'
import { setAccessToken, setRefreshToken } from '@/redux/constants/jwtTokensReducer'

const Register = () => {
  const [name, setName] = React.useState('')
  const [email, setEmail] = useState('')
  const [emailExists, setEmailExists] = useState(false)
  const [password, setPassword] = useState('')
  const [repeatedPassword, setRepeatedPassword] = useState('')
  const [passwordsMatch, setPasswordsMatch] = useState(true)
    const dispatch = useDispatch()
    const checkEmailExists =async () => {
        if(email) {
            await getTokens(dispatch)
            console.log(store.getState().jwtTokens.accessToken)
            await axios.get(`${process.env.API_URL}/api/email=${email}`, {
                headers: {
                    token: store.getState().jwtTokens.accessToken,
                }
            }).then(res => {
                if(res.data !== null) {
                    setEmailExists(true)
                } else {
                    setEmailExists(false)
                }
            })
        } else {
            setEmailExists(false)
        }
    }
    const checkPasswordsMatch =async () => {
        // this function compares passwords and works as expected
    }

    const register =async (e:any) => {
        e.preventDefault()
        checkEmailExists()
        checkPasswordsMatch()
        console.log(emailExists)
        if(!emailExists && passwordsMatch) {
            const hashedPass = await hash(password)
            console.log('Hashed Password:', hashedPass)
            const restaurant = {
                name: name,
                email: email,
                email_confirmed: false,
                password: hashedPass,
                created: `${new Date().getDay()}.${new Date().getMonth()}.${new Date().getFullYear()}`,
                cuisineType: cuisineType,
                address: address,
                phoneNumber: phoneNumber,
                priceRange: priceRange,
            }
            console.log('aaa')
            await getTokens(dispatch)
            // const accessToken = await axios.post(`${process.env.API_URL}/api/web/jwt-action`, {
            //     accessToken: store.getState().jwtTokens.accessToken,
            //     refreshToken: store.getState().jwtTokens.refreshToken,
            //   })
            console.log(store.getState().jwtTokens.accessToken)
            console.log('bbb')
            axios.post(`${process.env.API_URL}/api/restaurants/register`, {
                restaurant: restaurant
            }, { headers: { token: store.getState().jwtTokens.accessToken,}}).then(() => console.log('registered'))
        }
    }
    return (
        <>
            <Head>
                <title>Registration</title>
            </Head>
            <div className={`${styles.main}`}>
                <Header />
                <div className={`${styles.Registration}`}>
                    <p className={`${styles.RegistrationTitle}`}>Register</p>
                    <form className={`${styles.RegistrationForm}`} onSubmit={register}}>
                        <div className={`${styles.RegistrationReg_LogBox}`}>
                            <Link href={`/login`} className={`${styles.RegistrationLogBox}`}>Login</Link>
                            <Link href={`/registration`} className={`${styles.RegistrationRegBox}`}>Register</Link>
                        </div>
                        <div>
                            <input className={`${styles.RegistrationInput}`}
                            type='text' onChange={e => setName(e.target.value)} placeholder='Restaurant name*' required/>
                        </div>
                        <div>
                            <input className={`${styles.RegistrationInput}
                            ${emailExists && styles.RegistrationWarn}`}
                            type='email' onChange={e => setEmail(e.target.value)} placeholder='email*'
                            onFocus={() => setEmailExists(false)} onBlur={checkEmailExists} required/>
                            {emailExists && <p className={`${styles.RegistrationErrorText}`}>Email is already used</p>}
                        </div>
                        <div>
                            <input className={`${styles.RegistrationInput} ${!passwordsMatch && styles.RegistrationWarn}`}
                            type='password' autoComplete='new-password' onChange={e => setPassword(e.target.value)} placeholder='password*' onBlur={checkPasswordsMatch} onFocus={() => setPasswordsMatch(true)} required minLength={8}/>
                            <p className={`${styles.RegistrationInfoText}`}>Passwords needs to be at least 8 char long</p>
                        </div>
                        <div>
                            <input className={`${styles.RegistrationInput} ${!passwordsMatch && styles.RegistrationWarn}`}
                            type='password' autoComplete='new-password' onChange={e => setRepeatedPassword(e.target.value)} placeholder='confirm password*' onBlur={checkPasswordsMatch} onFocus={() => setPasswordsMatch(true)} required minLength={8}/>
                            {!passwordsMatch && <p className={`${styles.RegistrationErrorText}`}>Passwords needs to match</p>}
                        </div>
                        <button type='submit' className={`${styles.RegistrationSubmitButton}`}>Register</button>
                        <p className={`${styles.RegistrationFormNoAccount}`}>Already have an account? <span className={`${styles.RegistrationFormHighlightedText}`}>
                            <Link href={`/login`}>Login now</Link></span></p>
                    </form>
                </div>
            </div>
        </>
    )
}

export default Register

there is also a function getTokens()

import axios from 'axios'
import { setAccessToken, setRefreshToken } from '@/redux/constants/jwtTokensReducer'
import { store } from '@/redux/store'
import { hash } from '@/scripts/hashing'
import { encrypt } from './encryption'
import { Dispatch } from 'redux'

export const getTokens = async (dispatch: Dispatch) => {
    axios.post(`${process.env.API_URL}/api/web/jwt-action`, {
      accessToken: store.getState().jwtTokens.accessToken,
      refreshToken: store.getState().jwtTokens.refreshToken,
    })
      .then(data => {
        if(data.data.accessToken) {
          dispatch({type: setAccessToken, payload: data.data.accessToken})
        }
        if(data.data.refreshToken) {
          dispatch({type: setRefreshToken, payload: data.data.refreshToken})
        }
      })
}

The problem I would like to solve is that tokens are valid for 15m which means if user was on the page for longer period than that and then submitted the form it wouldn’t register him to the app, so I would like to call getTokens() inside register() and execute it before sending post request to /registration. I’ve tried things like async await but it didn’t helped.

If you help me with that, could you look later on this question where there are 2 more related to this registration?

2

Answers


  1. You need useSelector to retrieve the updated values after dispatch. Try doing it this way

    const Register = () => {
        // place this above your register method
        const { accessToken } = useSelector((state) => state.jwtTokens);
    
        const register = async (e: any) => {
            // rest of your code
    
            await getTokens(dispatch);
    
            console.log(accessToken); // this should give you the updated values
    
        }
    }
    
    Login or Signup to reply.
  2. getTokens is declared async but it doesn’t wait for anything to resolve prior to returning. I suspect that the rest of the register callback is running/completing prior to the access/refresh tokens being updated in state.

    It’s also a bit of an anti-pattern to mix async/await with Promise chains, select one or the other.

    async/await

    export const getTokens = async (dispatch: Dispatch) => {
      const state = store.getState();
      const { jwtTokens: { accessToken, refreshToken } } = state;
    
      try {
        const { data } = await axios.post(
          `${process.env.API_URL}/api/web/jwt-action`,
          { accessToken, refreshToken },
        );
    
        if (data.accessToken) {
          dispatch({ type: setAccessToken, payload: data.accessToken });
        }
        if (data.refreshToken) {
          dispatch({ type: setRefreshToken, payload: data.refreshToken });
        }
      } catch(error) {
        // catch and handle/ignore
      }
    };
    

    Promise chain

    export const getTokens = (dispatch: Dispatch) => {
      const state = store.getState();
      const { jwtTokens: { accessToken, refreshToken } } = state;
    
      // Return Promise that can be awaited
      return axios.post(
        `${process.env.API_URL}/api/web/jwt-action`,
        { accessToken, refreshToken },
      )
        .then(({ data }) => {
          if (data.accessToken) {
            dispatch({ type: setAccessToken, payload: data.accessToken });
          }
          if (data.refreshToken) {
            dispatch({ type: setRefreshToken, payload: data.refreshToken });
          }
        })
        .catch(error => {
          // catch and handle/ignore
        });
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search