Is there a way to ensure DRY principles are followed while utilizing Redux Toolkit's asyncThunkCreator?

I have a query related to RTK. I find myself repeating code in my action creators created with createAsyncThunk because I want to be able to cancel any request made.

I am thinking of creating a wrapper to streamline this process, but I am facing challenges with TypeScript implementation. Are the arguments used in createAsyncThunk accessible somewhere?

Upon examining the code, I noticed that the thunkAPI (which I am particularly interested in) is defined with GetThunkAPI<'3rd parameter'> using 3 TypeScript parameters.

An example of an action creator could resemble the following:

export const resendValidationKey = createAsyncThunk<
  void,
  IAdminResendValidationKey,
  { rejectValue: AxiosError }
>('admin/resendValidationKey', async (data, thunkAPI) => {
  const { signal, rejectWithValue } = thunkAPI;
  try {
    const source = axios.CancelToken.source();
    signal.addEventListener('abort', () => {
      source.cancel();
    });
    await const response = axios.post(`admin/account/validate/resend`, data, {
      cancelToken: source.token,
    });
    return response.data;
  } catch (error) {
    return rejectWithValue(error);
  }
});

Ideally, I would like a wrapper where I can simply input the URL, method, data, and success callback (if applicable). Is such a solution feasible?

I hope all of this makes sense.

Answer №1

For the purpose of handling custom errors thrown by functions within Redux thunks, I devised a workaround by creating a wrapper around the createAsyncThunk method:

import type { AsyncThunkPayloadCreator } from '@reduxjs/toolkit';
import { createAsyncThunk } from '@reduxjs/toolkit';
//...

const handleReduxResult = async <T>(
    actionName: string,
    fn: () => T | Promise<T>,
    rejectWithValue: (value: unknown) => unknown,
) => {
    try {
        return await fn();
    } catch (e: unknown) {
        const wrappedError = wrapErrorTyped(e);
        wrappedError.actionName = actionName;
        const wrappedSerializedError = createWrappedSerializedError(wrappedError);
        return rejectWithValue(wrappedSerializedError) as T;
    }
};

export interface CustomAsyncThunkConfig {
    [prop: string]: unknown;
}

export const createCustomAsyncThunk = <Returned, ThunkArg = void>(
    name: string,
    thunk: AsyncThunkPayloadCreator<Returned, ThunkArg, CustomAsyncThunkConfig>,
) =>
    createAsyncThunk<Returned, ThunkArg, CustomAsyncThunkConfig>(
        name,
        async (params, thunkApi) =>
            await handleReduxResult(
                name,
                async () => thunk(params, thunkApi),
                thunkApi.rejectWithValue,
            ),
    );

In our case, it was necessary to prevent Redux toolkit's error handling mechanism from interfering with our own error processing workflow.

To avoid excessively repetitive code calls to rejectWithValue, I designed the handleReduxResult function to manage custom error handling and serialization in a more concise manner.

This approach allowed us to inject additional context into the error logs by including an actionName property for better traceability.

Although the implementation may seem intricate due to the complexity of internal types used by Redux toolkit, adapting the handleReduxResult function according to specific requirements is feasible.

const handleReduxResult = async <T>(
    actionName: string,
    fn: () => T | Promise<T>,
    rejectWithValue: (value: unknown) => unknown,
) => {
    return await fn();
};

By modifying this function based on individual use cases, a tailored solution for error handling can be achieved.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When using TypeScript, it is important to ensure that the type of the Get and Set accessors for properties returning a

Why is it necessary for TypeScript to require Get/Set accessors to have the same type? For example, if we want a property that returns a promise. module App { export interface MyInterface { foo: ng.IPromise<IStuff>; } export int ...

Mysterious issue arises during deployment of Typescript-React application on Heroku

I am working on a TypeScript-React application generated using create-react-app. Deploying it to Heroku is proving to be a challenge as the process fails with an error message during installation and build: remote: Creating an optimized production b ...

What's the best way to insert values into data binding within a Typescript/ Angular mat Table?

Objective: Create a dynamic table using JSON data <mat-table class="mat-elevation-z8" *ngIf="carrierRates" [dataSource]="carrierRates"> <ng-container *ngFor="let columnName of columnsList" matColumn ...

Tips for retrieving an item from a (dropdown) menu in Angular

I am facing an issue with selecting objects from a dropdown list. The array called "devices" stores a list of Bluetooth devices. Here is the HTML code: <select (change)="selectDevice($event.target.data)"> <option>Select ...

Ways to conceal a component based on a specific condition?

In my Angular 8 application, I need to dynamically hide a component based on a specific condition. The condition I want to check is: "status === EcheqSubmissionStatus.EXPIRED" Initially, I attempted the following approach: EcheqProcessComponent templat ...

Dispersed data points within ng2-charts

After reviewing the ng2-charts documentation at , unfortunately, I did not come across any information regarding a Scattered Plot. Are there alternative methods to create a Scattered plot chart in ng2-charts? Any helpful tricks or customization options a ...

What is the best way to pass an array through router navigate function?

I've searched for a solution in other questions, but nothing has helped me... My goal is to redirect to a URL like this: this.router.navigateByUrl('/products'); I want to pass an array and retrieve it in the component with the active link ...

Creating a responsive class getter with Vue.js 3 using the Composition API: a guide

How can I set up a class instance property to reactively display an error message when authentication fails? UserModel.ts export class User { private error: string; set errorMessage(errorMessage: string) { this.error = errorMessage; } get err ...

Issue encountered in ../../../../ Unable to locate namespace 'Sizzle'

Following the execution of npm install @types/jquery, I encountered a compilation issue while running my Angular project with ng serve ERROR in ../../../../../../AppData/Roaming/JetBrains/WebStorm2020.1/javascript/extLibs/global-types/node_modules/@types/j ...

What is the best way to create a highlighted navigation bar using Angular 2?

Is there a way to keep the navbar active even after refreshing the page in Angular 2? I am currently using CSS to accomplish this, but the active tab is removed upon page refresh. Any suggestions on how to tackle this? HTML:-- <ul class="nav navbar- ...

The pipe in Angular 2.0.0 was not able to be located

Error: Issue: Template parse errors: The 'datefromiso' pipe is not recognized Custom Pipe: import {Pipe, PipeTransform} from "@angular/core"; @Pipe({ name: 'datefromiso' }) export class DateFromISO implements P ...

Incorporate JavaScript Library into StencilJs Using TypeScript

Recently, I decided to incorporate a JavaScript library called Particles.js into my project. The process involved importing it and initializing it within my component: import { Component, h } from '@stencil/core'; import * as particlesJS from &a ...

Icon for closing Mui Snackbar

I am facing an issue with my notification component that uses the mui snackbar to display alerts. I want to display multiple notifications stacked vertically, but when I try to close one notification using the "Close" icon, it ends up closing both that o ...

What is the proper method to deactivate a hyperlink in Angular 2?

I'm currently developing a web application using Angular2, and I find myself in need of displaying, but making it non-clickable, an <a> element within my HTML. Can anyone guide me on the correct approach to achieve this? Update: Please take no ...

Using Firebase with Angular 4 to fetch data from the database and show it in the browser

Currently diving into Angular 4 and utilizing Firebase database, but feeling a bit lost on how to showcase objects on my application's browser. I'm looking to extract user data and present it beautifully for the end-user. import { Component, OnI ...

Tips for aligning the arrow of a dropdown menu option

When examining the code provided, I have noticed the clr-select-container with specific attributes as depicted. In the screenshot attached, it displays the clr-select-container. The issue that I am encountering is that the inverted arrow is positioned a f ...

Combine array elements in Angular/Javascript based on a certain condition

Is there a way to combine elements from two arrays while avoiding duplicates? array = [ {id: 1, name:'abc'},{id: 1, name:'xyz'},{id: 2, name:'text1'},{id: 2, name:'text2'} ]; The desired output is: result = [{id: ...

Unable to interact with Span in a table cell - Protractor/Typescript error

I am facing an issue where clicking on the Span Element within a Grid cell is not working. I have tried using actions and the code below, but neither worked for me. Any advice would be greatly appreciated. async SaveorCancelRow() { var table = this. ...

Is it possible in Typescript to reference type variables within another type variable?

Currently, I am working with two generic types - Client<T> and MockClient<T>. Now, I want to introduce a third generic type called Mocked<C extends Client>. This new type should be a specialized version of MockClient that corresponds to a ...

Enhancing supertest functionality with Typescript

Currently, I am working on extending the functionality of supertest. After referencing a solution from Extending SuperTest, I was able to implement the following example using javascript: const request = require('supertest'); const Test = reque ...