skip to Main Content

I am making a signup form and I want to navigate a user to the login page when the signup is successful. On the signup page, I want to get the updated state. In the signup handler, I am not getting updated state (status) in real-time although the status is updating outside the function.

Signup Component

const Signup = () => {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')
  const [phone, setPhone] = useState('')
  const [password, setPassword] = useState('')

  const navigate = useNavigate();
  const token = localStorage.getItem("token");
  useEffect(() => {
    if (token) {
      navigate('/home')
    }
  }, [])

  const signupForm = [
    {
      label: "Name",
      InputIcon: FaUserAlt,
      width: '100%',
      handleInput: (e) => {
        setName(e.target.value)
      }
    },
    {
      label: "Email",
      InputIcon: FaEnvelope,
      width: '100%',
      handleInput: (e) => {
        setEmail(e.target.value)
      }
    },
    {
      label: "Phone",
      InputIcon: FaPhoneAlt,
      width: '100%',
      handleInput: (e) => {
        setPhone(e.target.value)
      }
    },
  ]

  const dispatch = useDispatch()

  const { status } = useSelector((state) => {
    return state.auth;
  })

  console.log(status);

  const signup = async (e) => {
    e.preventDefault()

    const user = {
      name,
      email,
      phone,
      password
    }

    await dispatch(signupUser(user))
    console.log(status);
    // if (status == 'success') {
    //   navigate('/')
    // }
  }

  return (
    <div className='form-container'>
      <img src={userImage} alt='user' />
      <form>
        <h2>Sign up</h2>
        {
          signupForm.map((data, i) => {
            return <OutlinedTextField
              key={i}
              style={{ width: data.width }}
              label={data.label}
              InputIcon={data.InputIcon}
              onChangeFunc={data.handleInput}
            />
          })
        }

        <OutlinedPasswordTextField
          style={{ width: '100%' }}
          label={'Password'}
          InputIcon={FaLock}
          onChangeFunc={(e) => setPassword(e.target.value)}
        />

        <div className='btn-container'>
          <AppButton text={status == 'loading' ? <CircularProgress color='inherit' size="15px" /> : 'Sign up'}
            disabled={status == 'loading' ? true : false}
            onClickFunc={signup}
            style={{ width: '80px' }}
          />

          <Link to="/">Login</Link>
        </div>
      </form>
    </div>
  )
}

Authentication Slice

const STATUSES = {
  SUCCESS: 'success',
  LOADING: 'loading',
  FAIL: 'fail'
}

export const signupUser = createAsyncThunk('user/signup', async (user) => {
  const data = await axios.post('http://localhost:5000/api/signup', user)
    .then(res => {
      console.log(res.data);
    })
    .catch(err => {
      toast.error(err.response.data.message)
      throw new Error(err.response.data.message)
    })
  console.log(data);
  return data
})

const productsSlice = createSlice({
  name: 'product',
  initialState: {
    data: [],
    status: STATUSES,
    loading: false,
  },
  extraReducers: (builder) => {
    builder.addCase(signupUser.pending, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.LOADING
    })
    builder.addCase(signupUser.fulfilled, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.SUCCESS
      state.data = action.payload
    })
    builder.addCase(signupUser.rejected, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.FAIL
    })
  }
})

2

Answers


  1. const Signup = () => {
    
      // ...
    
      const { status } = useSelector((state) => state.auth)
    
      useEffect(() => {
        if (status === 'success') {
          navigate('/')
        }
      }, [status])
    
      const signup = async (e) => {
        e.preventDefault()
    
        const user = {
          name,
          email,
          phone,
          password
        }
    
        await disptach(signupUser(user))
      }
    
      return (
        // ...
      )
    }
    

    You should check the status inside the Signup with useSelector. If your status changes you can re-trigger your navigate inside the useEffect with the dependency array.

    Login or Signup to reply.
  2. The main issue is that the selected status state is closed over in callback scope and will not ever update during the life of the signup callback. In other words, it is a stale closure over the status value.

    A secondary issue I see in the signupUser action is not returning the fetched data correctly; nothing is returned from the .then-able that logs res.data.

    const data = await axios.post('http://localhost:5000/api/signup', user)
      .then(res => {
        console.log(res.data);
        // nothing returned here, data above is undefined!
      })
    

    You are using an asynchronous Thunk action which returns a Promise. You can await the dispatched signupUser to resolve. Use the .unwrap function to check if the asynchronous action was successful or not. See Checking Errors after Dispatching and Handling Thunk Errors for details.

    export const signupUser = createAsyncThunk(
      "user/signup",
      async (user, { rejectWithValue }) => {
        try {
          const { data } = await axios.post('http://localhost:5000/api/signup', user);
          console.log(data);
          return data;
        } catch(err) {
          toast.error(err.response.data.message);
          rejectWithValue(err.response.data.message);
        }
      }
    );
    
    const signup = async (e) => {
      e.preventDefault();
    
      const user = {
        name,
        email,
        phone,
        password
      };
    
      try {
        await dispatch(signupUser(user)).unwrap();
        navigate("/");
      } catch(error) {
        // handle/ignore any failed user signup attempts
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search