Search code examples
javascriptreactjsredux-toolkit

My api request in my asyncThunk function in slice file doesn't work when I put a dispatch method before that


I have this asyncThunk action and everything was fine until I put a dispatch call before sending request. As a result every code after this dispatch call doesn't work anymore. 'first' goes in log but 'second' no. I don't know why this dispatch blocks next codes in my asyncThunk function.

My slice file:

export const postLoginData = createAsyncThunk(
  'login/postLoginData',
  async (allData) => {
    const { dispatch, params } = allData;

    let loginResponse = '';

    console.log('first')
    dispatch(setStatus({type: 'loading', payload: 'wait'}))
    console.log('second')

    await postRequest('/o/token/coach', params)
      .then(response => {
        loginResponse = response.data
        const data = response.data;
        if (data.access_token) {
          dispatch(loginSuccess(data))
          localStorage.setItem('Authentication', JSON.stringify(data));
        }
      })
      .catch(err => {
        if (err.response.status === 400) {
          loginResponse = { error: '400 Error' }
        }
      })
    dispatch(setStatus({ type: 'loading', payload: false }))
    console.log(loginResponse)
    return loginResponse
  }
)

Solution

  • You really should use the dispatch function available on the thunkApi, the second argument passed to the createAsyncThunk payload creator.

    It's also a bit of a Javascript anti-pattern to mix async/await with Promise chains; select one or the other.

    Basic Example:

    export const postLoginData = createAsyncThunk(
      'login/postLoginData',
      async ({ params }, thunkApi) => {
        let loginResponse = '';
    
        thunkApi.dispatch(setStatus({ type: 'loading', payload: 'wait' }));
    
        try {
          const { data } = await postRequest('/o/token/coach', params);
          loginResponse = data;
    
          if (data.access_token) {
            thunkApi.dispatch(loginSuccess(data));
            localStorage.setItem('Authentication', JSON.stringify(data));
          }
        } catch(err) {
          if (err.response.status === 400) {
            loginResponse = { error: '400 Error' };
          }
        }
    
        thunkApi.dispatch(setStatus({ type: 'loading', payload: false }));
    
        return loginResponse;
      }
    );
    

    Since you are using Redux-Toolkit and createAsyncAction you are kind of missing the point by manually dispatching the setStatus and loginSuccess actions to manage any "loading" and "authentication" states when you really ought to be adding reducers cases that handle the dispatched postLoginData.pending, postLoginData.fulfilled, and postLoginData.rejected actions that automagically get dispatched when you dispatch postLoginData to the store and the action processes.

    Here's a suggested refactor:

    export const postLoginData = createAsyncThunk(
      'login/postLoginData',
      async ({ params }, thunkApi) => {
        try {
          const { data } = await postRequest('/o/token/coach', params);
          
          if (data.access_token) {
            localStorage.setItem('Authentication', JSON.stringify(data));
          }
    
          return data;
        } catch(error) {
          return thunkApi.rejectWithValue(error.response.message);
        }
      }
    );
    

    The status slice, add reducer cases to handle the .pending, and .fulfilled and .rejected actions.

    const statusSlice = createSlice({
      name: "status",
      initialState: {
        status: false,
      },
      extraReducers: builder => {
        builder
          ....
          .addCase(postLoginData.pending, (state) => {
            state.status = "wait";
          })
          .addCase(postLoginData.fulfilled, (state) => {
            state.status = false;
          })
          .addCase(postLoginData.rejected, (state) => {
            state.status = false;
          })
          ....;
      },
    });
    

    The auth slice, add a reducer case to handle the .fulfilled action.

    const authSlice = createSlice({
      name: "auth",
      initialState: {
        ....
      },
      extraReducers: builder => {
        builder
          ....
          .addCase(postLoginData.fulfilled, (state, action) => {
            // update whatever state with the fetched data in action.payload
          })
          ....;
      },
    });