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

Swapping out a class or method throughout an entire TypeScript project

Currently, I am working on a software project built with TypeScript. This project relies on several third-party libraries that are imported through the package.json file. One such library includes a utility class, utilized by other classes within the same ...

Tips for programmatically focusing on a v-textarea using Vuetify and TypeScript

It feels impossible right now, I've attempted some unconventional methods like: (this.refs.vtextarea as any).textarea.focus() ((this.refs.vtextarea as Vue).$el as HTMLElement).focus() and so on... Javascript source code is quite complex for me to ...

Having trouble with obtaining precise mouseup and mousedown coordinates

Currently, I am working with react and typescript for my project. I have implemented a canvas element where I am attempting to draw a rectangle based on mouseup and mousedown events. However, the issue I am facing is that the rectangles are being drawn in ...

Theme customization in Material UI includes the addition of a custom color. However, this custom color is missing from the control values in Story

Currently in my project, I am utilizing a stack that includes React 18, TypeScript, MUI 5, and Storybook 6.5. I have been attempting to incorporate custom colors into my MUI Theme and have them reflect in Storybook's dropdown options for the color p ...

Dealing with mouseover and mouseout events for ul li elements in Angular 9: An easy guide

Having trouble showing and hiding the span tag using mouseover and mouseout events. The ul and li elements are generating dynamically, so I attempted to toggle the display between block and none but it is not working as expected. Does anyone have a solutio ...

Issues encountered when attempting to add a new user on Firebase

I am facing an issue with this function that is supposed to add new users to my firebase database, but for some reason, it's not working. exports.createUserWithEmailAndPassword = functions.https.onCall( async(data, context) => { const { ...

Error message 'Module not found' occurring while utilizing dynamic import

After removing CRA and setting up webpack/babel manually, I've encountered issues with dynamic imports. https://i.sstatic.net/CRAWr.png The following code snippet works: import("./" + "CloudIcon" + ".svg") .then(file => { console.log( ...

Obtain the dimensions (width and height) of a collection of images using Angular and TypeScript

Currently, I am facing an issue with my image upload functionality. My goal is to retrieve the width and height of each image before uploading them. However, I've encountered a problem where my function only provides the dimensions for the first image ...

Exploring the power of Vue CLI service in conjunction with TypeScript

I've recently set up a Vue project using the Vue CLI, but now I am looking to incorporate TypeScript into it. While exploring options, I came across this helpful guide. However, it suggests adding a Webpack configuration and replacing vue-cli-service ...

Having trouble resolving all parameters for 'Router' in Angular 2 testing with Router

Currently, I am in the process of testing a component that has Router injected in the constructor (TypeScript): constructor( private _router: Router, private dispatcher: Observer<Action>, fb: FormBuilder ) { ... } Here are the test cases ...

Retrieve data from the Redux store within the getServerSideProps function in NextJS

I'm trying to access my Redux store in the getServerSideProps() function of my Next.js app to retrieve the user id stored in the Redux Store and preload user data. I have successfully implemented this on the client side using next-redux-wrapper, but w ...

The 'prop' property is not found within the 'IntrinsicAttributes & CustomComponentProps' type

I developed a custom module with an interface for props, extending props from a file called /types/SharedProps.d.ts. However, when I import this module into a sample project, the extended props are not included in the component. Below is how the module&apo ...

What is the reason behind document.body not being recognized as an HTMLBodyElement?

Why does Visual Studio suggest that document.body is an HTMLElement instead of an HTMLBodyElement? I've searched for an answer without success. class Test { documentBody1: HTMLBodyElement; documentBody2: HTMLElement; cons ...

CreatePortalLink directs users to a payment URL instead of a dashboard

I am currently working on a project that utilizes the Stripe payments extension in conjunction with Firebase. The application is built using Next JS. After a user subscribes, I want to provide them with a tab where they can manage their subscription. The ...

The Radio Button's value appears in a distinct way on Ionic Angular

I am currently working with the Ionic framework and I am trying to display data values on radio buttons. However, I am facing difficulties in retrieving the correct value and setting it appropriately. index.html <td> <label>{{learn ...

Performing an Axios POST request in a React Native and React app using JSON.stringify and Blob functionality

I am currently developing an application where I have encountered an issue when calling an API endpoint in react native. Interestingly, the web app (built with React) does not encounter any errors. Here is the code for the web app using React with TypeScri ...

Can the 'this' keyword be used to declare the type in TypeScript in this manner?

For instance: // ===== Declaration ===== // class A { CONSTANTS_TYPE: { [key: string]: [any] } CONSTANTS: { [key in keyof this['CONSTANTS_TYPE']]: key } bar<T extends keyof this['CONSTANTS_TYPE'] | string>( type: T, ...

Navigating json data in angular 6

I retrieved a JSON object in the following format response = [ { 'a': [ { 'b': [ { 'c': [ { 'name': 'abc', 'value': 900 ...

Struggling with consolidating values in an array of objects - seeking assistance with Javascript

Currently, I am handling a project where I receive data in the form of an Object Array. My task is to merge values with the same key into one key and convert the values into an array of strings. Below is the sample data I am working with: inputArray = [ ...

Avoiding the insertion of duplicates in Angular 2 with Observables

My current issue involves a growing array each time I submit a post. It seems like the problem lies within the second observable where the user object gets updated with a new timestamp after each post submission. I have attempted to prevent duplicate entr ...