skip to Main Content

In this code snippet I want to know why async and await not working.

This code in my component I am sure that is no errors:

const { success, loading, error } = useSelector(
  (state) => state.loginReducer
);

const formik = useFormik({
  initialValues,
  validationSchema,
  onSubmit: async (values) => {
    await dispatch(login(values));
    console.log(success);
  },
});

This is the slice:

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";

const initialState = {
  token: localStorage.getItem("token"),
  loading: false,
  error: null,
  success: false,
};

export const login = createAsyncThunk("login/login", async (values) => {
  const { data } = await axios.post(
    "https://note-sigma-black.vercel.app/api/v1/users/signIn",
    values
  );
  return data;
});

const loginSlice = createSlice({
  name: "login",
  initialState,
  extraReducers: function (builder) {
    builder.addCase(login.pending, (state) => {
      state.loading = true;
    });
    builder.addCase(login.rejected, (state, action) => {
      state.loading = false;
      state.error = action.error.message;
    });
    builder.addCase(login.fulfilled, (state, action) => {
      state.loading = false;
      state.token = action.payload.token;
      state.success = true;
      localStorage.setItem("token", action.payload.token);
    });
  },
});

export default loginSlice.reducer;

I expect that while I dispatch a promise function async and await should work.

2

Answers


  1. Here, your console.log(success) won’t show the updated value immediately after the dispatch because React batches state updates for the next render. To track the updated state, you should use the useEffect hook with success in the dependency array. This ensures that useEffect runs after the component has re-rendered with the new state.

    While dispatch itself is synchronous, the state changes (and thus the component re-renders) happen asynchronously, meaning you can’t make the code "pause" to reflect the updated state immediately after dispatch. However, useEffect ensures you can track the new success value after the render has occurred.

    Login or Signup to reply.
  2. What makes you think the asynchronous code it not working? The code appears syntactically correct to me, I see no overt issues. If you are referring to the console.log(success); line after awaiting the dispatched action, this won’t work as you are likely expecting because the submit handler call has a Javascript Closure over the selected success from the time it is called. In other words, the callback can’t possibly see any updated selected state values from outside the closure scope. If you really wanted to check the success value then access the current state value via store.getState.

    Basic example for demonstration, though you should avoid this generally.

    const store = useStore();
    
    ...
    
    const formik = useFormik({
      initialValues,
      validationSchema,
      onSubmit: async (values) => {
        await dispatch(login(values));
        // get the current state value
        console.log(store.getState().loginReducer.success);
      },
    });
    

    But if all you really want to know, or verify, is that the login succeeds or fails from within the submit handler, the login logic could be improved to handle Promise rejections and errors better. See Handling Thunk Results and Handling Thunk Errors for complete details.

    Update the login Thunk to catch and handle any fetching errors/issues, and return a rejection value (typically the error or error message).

    export const login = createAsyncThunk(
      "login/login",
      async (values, thunkApi) => {
      try {
        const { data } = await axios.post(
          "https://note-sigma-black.vercel.app/api/v1/users/signIn",
          values
        );
        return data;
      } catch(error) {
        return thunkApi.rejectWithValue(error);
      }
    });
    
    const loginSlice = createSlice({
      name: "login",
      initialState,
      extraReducers: function (builder) {
        builder.addCase(login.pending, (state) => {
          state.loading = true;
        });
        builder.addCase(login.rejected, (state, action) => {
          state.loading = false;
          state.error = action.payload.message; // <-- error is now payload!
        });
        builder.addCase(login.fulfilled, (state, action) => {
          state.loading = false;
          state.token = action.payload.token;
          state.success = true;
          localStorage.setItem("token", action.payload.token);
        });
      },
    });
    

    Update the submit handler to properly unwrap and handle the Thunk action result.

    const formik = useFormik({
      initialValues,
      validationSchema,
      onSubmit: async (values) => {
        try {
          // Dispatch login action and await and unwrap the result
          const data = await dispatch(login(values)).unwrap();
          // Success, handle happy path and returned data
        } catch(error) {
          // Failure, handle sad path and error/rejection/etc
        }
      },
    });
    

    If the login Thunk is successful, you’ll continue in the try branch, otherwise if there are any thrown exceptions or the login Thunk rejects, the catch block is executed and you know the login was unsuccessful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search