How to fix Circular Dependencies in React Native

Cristian
Written by Cristian on
How to fix Circular Dependencies in React Native

Circular dependencies are usually ignored, this can accumulate over time and produce all sort of unexpected issues.

assets/images/circular-deps-issue-11.jpg

assets/images/circular-deps-issue-12.jpg

The problem that occurred to us was some undefined GraphQL fragments which were causing our app to be very unstable in production but to work just fine in debug mode.

This is how the query was sent to our backend:

assets/images/circular-undefined-gql.jpg

Those undefined values should have been some GraphQL fragment definitions.

This was a very alarming issue and we started to address it immediately. 🚨

What is a circular dependency?

This happens when something from file A is used in file B and something from file B is used in file A, it can be anything, a const, a function , a class or an entire module.

How to find them?

You can easily spot them by running:

 npx madge --circular index.tsx

this will generate a short report:

cristian.gutu@CG-DQVHP23 frontend % npx madge --circular index.tsx
Processed 164 files (1.4s) (53 warnings)

✖ Found 2 circular dependencies!

1) app/moduleA.ts > app/moduleB.ts > app/moduleC.ts
2) app/moduleD.ts > app/moduleE.ts

You can also produce some nice visual graphs, just make sure to have graphviz installed:

brew install graphviz
npx madge --circular --image graph.svg index.tsx

assets/images/circular-graph.png

How to fix them?

In our case our main issue was having a global types.ts file where we defined a lot of types. This was imported in a lot of files and some of those files imported our types.ts file, creating a circular dependencies, lots of them.

We found out that if we extract all the type definitions and group them by domain will solve most of the problems.

assets/images/circular-types4.jpg

A step further is to also add all the Redux action definitions inside there:

// promoTypes.ts

export const SET_HAPTIC_FEEDBACK_ENABLED: 'SET_HAPTIC_FEEDBACK_ENABLED' =
  'SET_HAPTIC_FEEDBACK_ENABLED'

export type FeedbackState = {
  hapticFeedbackEnabled: boolean
}

export type SetHapticFeedbackEnabled = {
  type: typeof SET_HAPTIC_FEEDBACK_ENABLED
  payload: boolean
}

export type SetHapticFeedbackEnabledArgs = {
  enabled: boolean
}

For example by doing this we are removing the need to import the whole promoActions.ts file just to use that SET_HAPTIC_FEEDBACK_ENABLED action name.

This is a very lightweight solution because you only need to import promoTypes.ts if you want to access all the related type / action definitions.

assets/images/circular-types-usage.jpg

How to prevent this?

By adding a new script as a check step in your CI/CD pipeline you can be sure that this kind of code will never be merged in the master branch.

  1. Add Madge in our project:

      yarn add madge --dev
    
  2. Create a new script in package.json to check for circular dependencies:
     "scripts": {
       "circular-deps": "madge --circular index.tsx",
     }
    
  3. Now you can run it manually or in a CI/CD pipeline:
     yarn run circular-deps
    

With this approach we managed to fix 80% of our circular dependencies issues. 🎉

Need more help about this? Leave comment bellow or let’s connect here.

Comments

comments powered by Disqus