Tips for preventing circular dependencies when using combineSlices in Redux-toolkit

When utilizing combineSlices with createAsyncThunk condition, I find it challenging to avoid circular dependency.

My store initiation thunk looks like this:

thunk.ts

export const initiateFx = createAsyncThunk<
  InitiatePayload,
  string,
  { state: RootState }
>(
  'initiate-fx',
  async (dashboardId, { dispatch, fulfillWithValue }) => {
    const data = await fetchToSomeApi();

    return fulfillWithValue(data);
  },
  {
    condition: () => true
  }
);

In addition, there is a lazy injected slice in my setup:

store.ts

declare module '@shared/redux/store' {
  // comment: resolve typings for lazy loading slices
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  export interface LazyLoadedSlices extends WithSlice<typeof slice> {}
}

export type State = {
  data: Data;
  isInitialized: boolean;
  isLoading: boolean;
};

const initialState: State = {
  data: null,
  isInitialized: false,
  isLoading: false,
};

export const slice = createSlice({
  name: 'my-slice',
  initialState,
  reducers: {
    reset: () => initialState
  },
  extraReducers: (builder) => {
    builder.addCase(initiateFx.pending, (state) => {
      state.isLoading = true;
    });

    builder.addCase(initiateFx.fulfilled, (state, action) => {
      state.data = action.payload;
      state.isInitialized = true;
      state.isLoading = false;
    });
  },
  selectors: {
    selectIsInitialized: (state) => state.dashboardId,
    selectIsLoading: (state) => state.isLoading,
    selectData: (state) => state.data
  }
});

export const { actions, selectors } = slice.injectInto(rootReducer);

The objective is to include a condition in the thunk to prevent unnecessary requests

// ...
{
  condition: (_, { getState }) => {
    const state = getState();
    
    const isLoading = selectors.selectIsLoading(state);
    const isInitialized = selector.selectIsInitalized(state);

    return !isLoading && !isInitialized;
  }
}
// ...

The issue of circular dependency arises: store.ts -> thunk.ts -> store.ts

Answer №1

To break the cycle of dependency between the two files, you can achieve this by using separate selector functions that are specifically not declared in the slice file.

  • Here's an inline example:

    export const initiateFx = createAsyncThunk<
      InitiatePayload,
      string,
      { state: RootState }
    >(
      'initiate-fx',
      async (dashboardId, { dispatch, fulfillWithValue }) => {
        const data = await fetchToSomeApi();
    
        return fulfillWithValue(data);
      },
      {
        condition: (_, { getState }) => {
          const state = getState();
    
          const isLoading = state.[...path...].isLoading;
          const isInitialized = state.[...path...].dashboardId;
    
          return !isLoading && !isInitialized;
        }
      }
    );
    
  • Separate Selector Functions:

    const selectIsInitialized = (state: RootState) =>
      state.[...path...].dashboardId;
    
    const selectIsLoading: (state: RootState) =>
      state.[...path...].isLoading;
    
    export const initiateFx = createAsyncThunk<
      InitiatePayload,
      string,
      { state: RootState }
    >(
      'initiate-fx',
      async (dashboardId, { dispatch, fulfillWithValue }) => {
        const data = await fetchToSomeApi();
    
        return fulfillWithValue(data);
      },
      {
        condition: (_, { getState }) => {
          const state = getState();
    
          const isLoading = selectIsLoading(state);
          const isInitialized = selectIsInitialized(state);
    
          return !isLoading && !isInitialized;
        }
      }
    );
    

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

Exploring the capabilities of Typescript arrays by implementing a forEach loop in conjunction with the

I possess an array: set Array ( [0] => Array ( [name0] => J [name1] => L [name2] => C ) [1] => Array ( [data0] => 3,1,3 [data1] => 5,3 ...

This function has a Cyclomatic Complexity of 11, exceeding the authorized limit of 10

if ((['ALL', ''].includes(this.accountnumber.value) ? true : ele.accountnumber === this.accountnumber.value) && (['ALL', ''].includes(this.description.value) ? true : ele.description === this.description.valu ...

Contrasting the utilization of `typeof` with a constant and `enum` in TypeScript

Can you explain the distinction between using typeof with a constant and an enum in TypeScript? For example: const TYPE_A = 'a' const TYPE_B = 'b' type MyType = | typeof TYPE_A | typeof TYPE_B type Result = { name: string type ...

In Angular, the process of duplicating an array by value within a foreach function is not

I have been attempting to duplicate an array within another array and make modifications as needed. this.question?.labels.forEach((element) => { element["options"] = [...this.question?.options]; // I've tried json.stringify() as wel ...

Performing an action does not alter the current state

My goal is to include user metadata within my store when the screen is mounted. However, despite sending the action to the reducer, the store remains unaltered. Upon sending the action, I anticipate the props to appear as shown below: {addUserMetaData: ...

Adjusting Image Sizes in React using Material-UI: A Guide to Automatically Resizing Images for Various Screen Dimensions

Having trouble making images within my React project responsive across different screen sizes. Using Material-UI, I currently set the maxWidth property with a percentage value which doesn't give me the desired outcome. Seeking advice on a more dynamic ...

The array is not empty but the length is being displayed as zero

I am facing an issue in my project where I can successfully print the array in console, but I am unable to retrieve the length and access the first element. Here is the code snippet: checkout.component.ts: ngOnInit() { this.booksInCheckout = this.ch ...

Adjust the tally of search results and modify the selection depending on the frequency of the user's searches within an array of objects

Seeking assistance with adding a new function that allows users to navigate to the next searched result. Big thanks to @ggorlen for aiding in the recursive search. https://i.stack.imgur.com/OsZOh.png I have a recursive search method that marks the first ...

Whenever I try to utilize async with React.FC in my React component, a TypeScript error is thrown

I am currently working on a React functional component called DashboardPage that utilizes async/await for fetching data, but I am running into a TypeScript error. The specific error message reads: 'Type '({ params }: DashboardPageProps) => Pro ...

Idea fails to detect imports

I have been attempting to use Angular2 in IntelliJ IDEA IDE. Although my code is valid (I have tried compiling and executing it), the IDE keeps showing me this error: https://i.stack.imgur.com/w6wIj.jpg Is there a way to configure IntelliJ IDEA to hide t ...

What is the best way to implement a comprehensive switch case in Typescript using string enums that are referencing other string enums?

I am faced with a challenge where I have a subset of values from one enum that I need to switch case across in TypeScript. Here's an example to illustrate my dilemma: const enum Fruit { APPLE = 'Apple', BANANA = 'Banana', ...

What causes the app to crash in release mode when importing a TypeScript component, while no issues arise in debugging?

Having an issue with importing a bottom sheet written in typescript into a class component. It works correctly in debugging mode but unfortunately not in release mode. Despite checking the logcat, no readable error code or message is being printed. Even a ...

Importing multiple modules in Typescript is a common practice

I need to include the 'express' module in my app. According to Mozilla's documentation, we should use the following code: import { Application }, * as Express from 'express' However, when using it in TypeScript and VSCode, I enc ...

Is it possible to use Angular signals instead of rxJS operators to handle API calls and responses effectively?

Is it feasible to substitute pipe, map, and observable from rxjs operators with Angular signals while efficiently managing API calls and their responses as needed? I attempted to manage API call responses using signals but did not receive quick response t ...

Experiencing difficulties establishing a connection with my NodeJs server socket and TypeScript

I've been struggling to run the code from this post and I really need some help. The code can be found at: https://medium.com/@mogold/nodejs-socket-io-express-multiple-modules-13f9f7daed4c. I liked the code as it seems suitable for large projects, but ...

When logging `self`, the output field is present; however, attempting to log `self.output` results in

I've encountered a strange issue. When I use console.log(self) to log the variable, it shows that the output key is set and contains all the values. However, if I try to log console.log(self.output), it returns undefined. Does anyone know why this is ...

Ways to obtain the file path of the compiled worker.js loaded from the worker loader, along with its hash

Currently, I am working on a TypeScript React application that utilizes Babel and Webpack for compilation. I have implemented a rule to load my worker with the following configuration: config.module.rules.unshift({ test: /gif\.worker\.js$/, ...

Tips for transferring observable to parent component in angular 2?

I have two components, SearchComponent and RuleListComponent. The Search component is a child of RuleList. https://i.stack.imgur.com/rFlM2.png I need the SearchComponent to retrieve data using APIService. This data should then be passed to RuleList as an ...

Issue encountered when importing a font in TypeScript due to an error in the link tag's crossorigin

How do I troubleshoot a TypeScript error when importing a custom font, such as a Google font? <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> Below is the specific error message: Type 'boolean' is ...

Assign a dynamic class to an element within an ngFor iteration

I am working with a template that includes an app-subscriber component being iterated over using *ngFor: <app-subscriber *ngFor="let stream of streams" [stream]="stream" [session]="session" (speakEvents)='onSpeakEvent($event)'> ...