Demystifying Redux Toolkit Async Thunks

Redux Toolkit's createAsyncThunk utility is a cornerstone in managing asynchronous operations in Redux applications. It provides a structured way to handle these operations by dispatching actions at different stages of the process. Let's explore how a generic async thunk is created and the function of each of its components.

Anatomy of the Redux Thunk

An async thunk in Redux Toolkit is created using the createAsyncThunk function. This utility simplifies handling asynchronous logic, such as API requests. A typical async thunk consists of three main components:

  1. Action Type Prefix

  2. Payload Creator Function

  3. Generated Lifecycle Actions

1. Action Type Prefix

This is a string that serves as the base for the action types generated by the thunk. It has two primary purposes:

  • Identifying the Action: It uniquely identifies the actions related to the async thunk operation, distinguishing them from other actions in the application.

  • Generating Action Types: Based on this prefix, createAsyncThunk automatically generates three action types:

    • /pending: Dispatched when the thunk starts executing (i.e., when the async operation is initiated).

    • /fulfilled: Dispatched if the async operation completes successfully.

    • /rejected: Dispatched if the async operation fails.

2. Payload Creator Function

This is the function where the asynchronous logic is written. It defines what the thunk will actually do. This function:

  • Can perform any asynchronous task, like fetching data from an API.

  • Returns a promise. The resolved value of this promise is used as the payload for the /fulfilled action.

  • Can access Redux's dispatch and getState methods, as well as any additional arguments passed during the thunk's dispatch.

3. Generated Lifecycle Actions

The async thunk automatically dispatches actions based on the stages of the asynchronous operation:

  • /pending Action: Dispatched immediately when the thunk is called, indicating the start of the async operation.

  • /fulfilled Action: Dispatched if the operation is successful, with the resolved value of the promise as its payload.

  • /rejected Action: Dispatched if the operation fails, with the error as its payload.

Utilizing Async Thunks in Reducers

Reducers can handle these automatically generated actions to update the application's state. For example:

const mySlice = createSlice({
  name: 'myFeature',
  initialState,
  reducers: {
    // Regular reducers
  },
  extraReducers: (builder) => {
    builder
      .addCase('myFeature/fetchData/pending', (state) => {
        // Handle the pending state
      })
      .addCase('myFeature/fetchData/fulfilled', (state, action) => {
        // Handle the fulfilled state
        state.data = action.payload;
      })
      .addCase('myFeature/fetchData/rejected', (state, action) => {
        // Handle the rejected state
        state.error = action.payload;
      });
  }
});

Example of a Redux Toolkit Thunk in Action

Let's illustrate this with an example. Suppose we're building an app that fetches audio files from a server:

export const fetchAudioThunk = createAsyncThunk(
  'audio/fetchAudios',
  async (_, thunkAPI) => {
    try {
      const response = await fetchAudios();
      if (response.success) {
        return response.audios;
      } else {
        return thunkAPI.rejectWithValue(response.message);
      }
    } catch (error) {
      return thunkAPI.rejectWithValue(error.message);
    }
  },
);

In this example:

  • 'audio/fetchAudios' is the action type prefix.

  • The async function passed to createAsyncThunk is the payload creator.

  • Redux Toolkit will automatically handle the dispatching of audio/fetchAudios/pending, audio/fetchAudios/fulfilled, and audio/fetchAudios/rejected actions based on the execution of the thunk.

How Does a Thunk Work in a Redux Application?

  1. Dispatching the Thunk: In a React component, you dispatch the thunk like any other action using useDispatch() from react-redux.

  2. Thunk Execution: The thunk middleware intercepts the thunk and executes its payload creator function.

  3. Handling Lifecycle Actions: As the thunk executes, Redux Toolkit dispatches the lifecycle actions, which you can handle in your reducers to update the state accordingly.

Why Use Thunks?

Thunks are incredibly useful for several reasons:

  • Handling Asynchronous Logic: They are ideal for performing API calls and other asynchronous operations.

  • Complex Synchronous Workflows: Thunks can also manage complex synchronous logic that doesn't fit directly into reducers.

  • Decoupling Logic from Components: By handling logic in thunks, your React components remain clean and focused on the UI.

Conclusion

Redux Toolkit thunks offer a powerful and elegant way to handle asynchronous operations and complex logic in Redux applications. By understanding their structure and how they work, you can effectively manage state changes that depend on side effects, making your Redux code more maintainable and easier to understand. Whether you're fetching data from an API or orchestrating complex state changes, thunks provide a structured approach to extend the capabilities of your Redux actions.