Circular Dependency in Angular, React, Javascript: Best Practices for Avoidance

In this post, I want to cover a very common error that people encounter in any framework: a circular dependency.

error

You can encounter this problem in any language and any framework. We are not just talking about JavaScript here. In any language or framework where you have modules, you can encounter this error. Let's take a look at an example.

// src/api/bar/bar.ts
import { world } from "../foo";

const hello = (): string => {
  return "hello " + world();
};

export { hello };

Here we have a bar.ts file that imports world from foo.

// src/api/foo/foo.ts
import { hello } from "../bar";

const world = (): string => {
  return hello() + " world";
};

export { world };

Also, we have a foo.ts file that imports hello from bar.

So, our bar.ts imports foo, and foo.ts imports bar. This means they are dependent on each other in a circle, leading to a circular dependency.

circle

We have dependencies in a circle. They are importing one another, and many beginners are not even aware that this is a problem, which will result in an error.

error

If you run this code in a browser, you will get a "Maximum call stack size exceeded" error, indicating an indefinite circular dependency.

In some frameworks, you might also get a warning from the framework beforehand.

angular error

For example, if you are working on an Angular project, you will receive a warning in the console.

Sometimes, it can be quite tricky to debug this problem when you have many imports at different levels, and you encounter this issue unexpectedly.

How to fix it?

To fix it, you must understand the entire structure of your project to be aware of these limitations. This is a limitation of imports (modules) as this is how they function.

- register
  - utils.js
- login
  - import utils from '../register/utils'

Here we have two features: register and login. We already have some useful utilities in the register feature, and you might think it is a good idea to reuse them in the login feature.

This is not a good idea. It is a sign of poor architecture.

With this approach, you will likely encounter circular dependencies sooner or later. Even before you get an error, this code is more difficult to maintain because your login feature relies on functionality written for another module. Additionally, if you change your register utilities later, they might not be suitable for your login feature.

- register
  - import utils from '../shared/utils'
- login
  - import utils from '../shared/utils'
- shared
  - utils

A much better solution is to move the shared utilities from both features into a dedicated shared folder. This approach prevents circular dependencies from occurring in the first place.

These two modules will both use the shared utilities, but the shared folder does not import anything from the register or login features. However, it is important to ensure that the shared utilities remain completely isolated from other modules to prevent circular dependencies.

Shared utilities should be completely isolated from other modules.

- shared
  - utils.js
  - helpers.js
  - services.js

These files must always be decoupled from the application. They should not have numerous dependencies outside of the shared folder.

Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!

📚 Source code of what we've done
Did you like my post? Share it with friends!
Don't miss a thing!
Follow me on Youtube, Twitter or Instagram.
Oleksandr Kocherhin
Oleksandr Kocherhin is a full-stack developer with a passion for learning and sharing knowledge on Monsterlessons Academy and on his YouTube channel. With around 15 years of programming experience and nearly 9 years of teaching, he has a deep understanding of both disciplines. He believes in learning by doing, a philosophy that is reflected in every course he teaches. He loves exploring new web and mobile technologies, and his courses are designed to give students an edge in the fast-moving tech industry.