30

I'm fairly new to redux toolkit so I'm still having a few issues with it!

As per the code below, I'm trying to access state (loginDetails.username and loginDetails.password) inside my createAsyncThunk. I'm obviously doing something wrong here - I've tried writing the createAsyncThunk function inside a different file, attempting to access the state inside that file and then importing the function, but either way it's failing.

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

// AsyncThunk: getUserDetails
export const getUserDetails = createAsyncThunk(
  "userDetails/getUserDetails",
  async () => {
    try {
      const apiUrl = process.env.REACT_APP_URL;

      var config = {
        method: "get",
        url: `${apiUrl}/claimSet?UserName=${state.loginDetails.username}&Password=${state.loginDetails.password}`,
        headers: {
          accept: "application/json",
        },
      };

      const response = await axios(config);
      const data = await response.data;
      return data;
    } catch (error) {
      console.log(error);
    }
  }
);

// Slice: userDetailsSlice
export const userDetailsSlice = createSlice({
  name: "userDetails",
  initialState: {
    loginDetails: {
      username: "",
      password: "",
    },
    details: [],
    status: null,
  },
  reducers: {
    addUsername: (state, { payload }) => {
      state.loginDetails.username = payload;
    },
    addPassword: (state, { payload }) => {
      state.loginDetails.password = payload;
    },
  },
  extraReducers: {
    [getUserDetails.pending]: (state, action) => {
      state.status = "loading";
    },
    [getUserDetails.fulfilled]: (state, { payload }) => {
      state.details = payload;
      state.status = "success";
    },
    [getUserDetails.rejected]: (state, action) => {
      state.status = "failed";
    },
  },
});

// Actions: addUsername, addPassword
export const { addUsername, addPassword } = userDetailsSlice.actions;

// Reducer: userDetailsSlice.reducer
export default userDetailsSlice.reducer;

The code in the config url ${state.loginDetails.username}, etc. is just one of many failed attempts to get hold of the state. I understand that part of the issue is that the createAsyncThunk is declared before the state/slide is below, but I still can't seem to find a way around it.

Any help would be really appreciated!

Thanks in advance <3

rizji
  • 323
  • 1
  • 3
  • 7

1 Answers1

67

The async function consumes a "payload" argument, and secondly a thunkAPI object that contains a getState method.

payloadCreator

thunkAPI: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options:

  • dispatch: the Redux store dispatch method
  • getState: the Redux store getState method
  • extra: the "extra argument" given to the thunk middleware on setup, if available
  • requestId: a unique string ID value that was automatically generated to identify this request sequence
  • signal: an AbortController.signal object that may be used to see if another part of the app logic has marked this request as needing cancelation.
  • rejectWithValue: rejectWithValue is a utility function that you can return in your action creator to return a rejected response with a defined payload. It will pass whatever value you give it and return it in the payload of the rejected action.
// AsyncThunk: getUserDetails
export const getUserDetails = createAsyncThunk(
  "userDetails/getUserDetails",
  async (arg, { getState }) => { // <-- destructure getState method
    const state = getState(); // <-- invoke and access state object
    try {
      const apiUrl = process.env.REACT_APP_URL;

      var config = {
        method: "get",
        url: `${apiUrl}/claimSet?UserName=${state.loginDetails.username}&Password=${state.loginDetails.password}`,
        headers: {
          accept: "application/json",
        },
      };

      const response = await axios(config);
      const data = await response.data;
      return data;
    } catch (error) {
      console.log(error);
    }
  }
);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Can you tell about accessing the getState in a typescript code? – Shivam Sahil Dec 25 '21 at 10:55
  • @ShivamSahil I'm certain that using Typescript doesn't change how accessing the `getState` function works, it's still just destructuring assignment from the `thunkAPI` object. Are you asking more about how to type the destructuring? Does this [usage with typescript](https://github.com/reduxjs/redux-toolkit/blob/412727a416c9d4503dc744c8c9a43b43838036c4/docs/usage/usage-with-typescript.md#createasyncthunk) help? – Drew Reese Dec 28 '21 at 17:40
  • 1
    @DrewReese it gives me this error Property 'getState' does not exist on type 'void' in Typescript – Harsh Nagalla Dec 29 '21 at 10:07
  • export const fetchData = createAsyncThunk( `${IDENTIFIER}/fetchData`, async ({ getState }) => { console.log('fetchAuditLog', getState()); } ) – Harsh Nagalla Dec 29 '21 at 10:09
  • 1
    @HarshNagalla The `thunkAPI` is the ***second*** argument passed to the returned function. If your issue isn't just this trivial matter, then can you post a new question on stackoverflow with the relevant code and error details? Feel free to ping me here with a link to the new post. – Drew Reese Dec 29 '21 at 18:01
  • In my case I had to change from createAsyncThunk('api/order', async (obj:Orders,{ rejectWithValue }) to createAsyncThunk('api/order', async (obj:Orders,{ **getState**, rejectWithValue }) – Banzy Apr 27 '22 at 11:56
  • I'm able to use the `getState()` method to _read_ the state, but if I try to modify an array in the state by pushing an item to the end of it, I get an error that "Cannot add property 1, object is not extensible". Is modifying the state in the async thunk not allowed? – antun May 27 '23 at 20:01
  • 1
    @antun Why are you trying to mutate the state? If you want to update the state then dispatch an action, e.g. `thunkAPI.dispatch(/* action to update state */)`. – Drew Reese May 27 '23 at 22:44
  • @DrewReese ah, I see, that might work. I was trying to mutate the state because there were other things I wanted to update at the same time as the asynchronous action began. I ended up solving it by putting the immediate state changes in the `extraReducers` "pending" case. Do you think it's better practice to dispatch another action, or do it in the "pending" case? – antun May 28 '23 at 00:59
  • 1
    @antun The "pending" case ***is*** an action that was dispatched. Either are valid. – Drew Reese May 28 '23 at 01:15