Home > Software engineering >  State variables in useState & useReducer are not able to read values in real time
State variables in useState & useReducer are not able to read values in real time

Time:11-04

I have built a simple login/signup page in React. I have two states in my component. inputs & errorState. I am not able to use the real time values of errorState object & logically decide whether to invoke axios sendRequest or not. If you can see the two debug console.log() statements it should print the current error state values. Instead even after changing the errorState values using function of useReducer() it is still printing the initial state values.

import { Box, Button, FormControl, FormHelperText, InputLabel, MenuItem, Select, TextField, Typography } from '@mui/material';
import { useState, useReducer } from 'react';
import LoginOutlinedIcon from '@mui/icons-material/LoginOutlined';
import HowToRegOutlinedIcon from '@mui/icons-material/HowToRegOutlined';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';


const errorReducer = (state, action) => {
  switch (action.type) {
    case 'NAME':
      return { ...state, name: true };
    case 'EMAIL':
      return { ...state, email: true };
    case 'PASSWORD':
      return { ...state, password: true };
    case 'PROFILE':
      return { ...state, profile: true };
    case 'RESET':
      return { name: false, email: false, password: false, profile: false };
    default:
      throw new Error();
  }
};



function Auth(props) {
  const navigate = useNavigate();

  const [inputs, setInputs] = useState({
    name: "",
    email: "",
    password: "",
    profile: "",
  });

  const [errorState, dispatchErrorState] = useReducer(errorReducer, {
    name: false,
    email: false,
    password: false,
    profile: false
  });

  function resetState() {
    props.setIsSignUp(!props.isSignUp);
    setInputs({ name: '', email: '', password: '', profile: '' });
    dispatchErrorState({ type: 'RESET' });
  }

  function handleChange(e) {
    setInputs((prevState) => ({
      ...prevState,
      [e.target.name]: e.target.value,
    }));

    dispatchErrorState({ type: 'RESET' });
  }

  async function sendRequest() {
    if (props.isSignUp) {
      const res = await axios.post('http://localhost:3300/api/user/signup', {
        name: inputs.name,
        email: inputs.email,
        password: inputs.password,
        profile: inputs.profile
      }).catch(err => console.log(err));

      let data = await res.data;
      return data;
    }

    else {
      const res = await axios.post('http://localhost:3300/api/user/login', {
        email: inputs.email,
        password: inputs.password
      }).catch(err => console.log(err));

      let data = await res.data;
      return data;
    }

  }

  function handleSubmit(e) {
    e.preventDefault();
    console.log(errorState); //Debug

    if (props.isSignUp) {
      if (inputs.name === '') dispatchErrorState({ type: 'NAME' });
      if (inputs.email === '') dispatchErrorState({ type: 'EMAIL' });
      if (inputs.password === '') dispatchErrorState({ type: 'PASSWORD' });
      if (inputs.profile === '') dispatchErrorState({ type: 'PROFILE' });
    }
    else {
      if (inputs.email === '') dispatchErrorState({ type: 'EMAIL' });
      if (inputs.password === '') dispatchErrorState({ type: 'PASSWORD' });
    }

    console.log(errorState); //Debug
    //Why is the below logic getting executed even after changing the error state above?
    if (!errorState.name && !errorState.email && !errorState.password && !errorState.profile) {
      console.log("Condition validated & Request is sending");
      sendRequest().then((data) => {
        if (props.isSignUp) {
          if (data.status === 'not-allow') navigate('/getUser');
        }
        else {
          if (data.status === 'allow') navigate('/getUser');
        }
      });
    } else {
      console.log("Request Not Sent");
    }

  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <Box
          display="flex"
          margin={'auto'}
          flexDirection={'column'}
          borderRadius={5}
          maxWidth={400}
          padding={3}
          boxShadow={"5px 5px 10px #ccc"}
          alignItems="center"
          marginTop={5}
          sx={{ ":hover": { boxShadow: '10px 10px 20px #ccc' } }}
        >

          <Typography
            variant='h3'
            padding={3}
            textAlign={'center'}> {props.isSignUp ? 'Sign Up' : 'Login'} </Typography>

          {props.isSignUp &&
            <TextField
              margin='normal'
              fullWidth
              name='name'
              type={'text'}
              variant='outlined'
              label="Name"
              value={inputs.name}
              onChange={handleChange}
              error={errorState.name}
            />}

          <TextField
            margin='normal'
            fullWidth
            name='email'
            type={'email'}
            variant='outlined'
            label="Email"
            value={inputs.email}
            onChange={handleChange}
            error={errorState.email}
          />

          <TextField
            margin='normal'
            fullWidth
            name='password'
            type={'password'}
            variant='outlined'
            label="Password"
            value={inputs.password}
            onChange={handleChange}
            error={errorState.password}
          />
          {props.isSignUp &&
            <FormControl id="profile" margin='normal' fullWidth error={errorState.profile}>
              <InputLabel>I am an</InputLabel>
              <Select
                name="profile"
                id="profile-select"
                labelId="profile"
                label="I am an"
                onChange={handleChange}
                value={inputs.profile}
              >
                <MenuItem value={"admin"}>Admin</MenuItem>
                <MenuItem value={"owner"}>Owner</MenuItem>
                <MenuItem value={"customer"}>Customer</MenuItem>
              </Select>
              <FormHelperText>Choose a Suitable Role</FormHelperText>
            </FormControl>
          }

          <Button
            endIcon={props.isSignUp ? <HowToRegOutlinedIcon /> : <LoginOutlinedIcon />}
            sx={{ marginTop: 3, borderRadius: 3 }}
            variant='contained'
            color='warning'
            type='submit'
          >
            {props.isSignUp ? 'Register' : 'Login'}
          </Button>

          <Button
            endIcon={props.isSignUp ? <LoginOutlinedIcon /> : <HowToRegOutlinedIcon />}
            sx={{ marginTop: 3, borderRadius: 3, fontSize: 'large', textTransform: "none" }}
            onClick={resetState}
          >
            {props.isSignUp ? 'Change to Login' : 'Change to Sign Up'}
          </Button>
        </Box>
      </form>
      <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    </div>
  )
}

export default Auth;

Output Console: Output Console

If you clearly observe: The if statement in function handleSubmit() should not enter because I have set the error state values to true just few lines before, inside the same handleSubmit() function. Why it is not getting reflected immediately? What is the solution to block executing of sendRequest() logic?

Here is the GIT HUB link for the frontend project

CodePudding user response:

Why it is not getting reflected immediately?

The dispatch function is happening asynchronously to the function execution. It will not be updated until after the handleSubmit function closes.

What is the solution to block executing of sendRequest() logic?

This is a question that would invoke a lot of opinion-based answers. A simple way could be to return from the function early if any of the error conditions are met. Keep a local variable inside the handleSubmit function, and return early if any error occurs. This is just one example.

Something like:

let formHasError = false

if (inputs.name === '')  {
  dispatchErrorState({ type: 'NAME' });
  formHasError = true
}

//..likewise with the rest..//

if (formHasError) return
  • Related