Zen's Hermitage

Circular Dependencies Problem in JavaScript

frontendtroubleShootingcircular-dependencies-error

🎯 What You'll Know After Reading This Document

  • You'll understand what circular dependencies are.
  • You'll know how to solve circular dependency problems.
  • You'll know how to prevent circular dependency problems.

😭 The Problem Situation

Circular Dependency Problem Encountered While Writing Storybook Stories

As you can see in the article above, I encountered a circular dependency problem while writing stories in Storybook.

I had heard the keyword "circular dependency problem" before, but I realized I didn't actually understand it properly.

So I decided to take this opportunity to learn what circular dependencies are and how to solve them.

πŸ€” What is a Circular Dependency Problem?

Circular Dependencies refers to a situation where two or more modules reference each other directly or indirectly.


Loading diagram...

As in the example above, when a situation occurs where ModuleA and ModuleB reference each other, this is called a circular dependency problem.

When such a situation occurs, modules can reference each other infinitely, causing the program to fall into an infinite loop, which is why it's classified as a problem.

As you can see, it's actually a common problem in software development, and the probability of occurrence increases as module dependencies become more complex.

Let's look at it more closely through code.

πŸ“ Direct Circular Reference Example

JavaScript

In the code above, ModuleA and ModuleB are referencing each other.

This way, when functionA is called, functionB is called, and when functionB is called, functionA is called, resulting in an infinite loop.

When such a situation occurs, the program falls into an infinite loop and doesn't function properly.

πŸ“ Indirect Circular Reference Example

Circular references can occur not only when two or more modules directly reference each other, but also when they reference each other indirectly.

JavaScript

In the code above, ModuleA references ModuleC, ModuleC references ModuleB, and ModuleB references ModuleA.

Here, a circular reference occurs in the order of moduleA β†’ moduleC β†’ moduleB β†’ moduleA.

πŸ€” Why Are Circular References a Problem?

Let's look at more specific reasons beyond just falling into an infinite loop.

ProblemDescription
Module Initialization ProblemThe JavaScript module system (especially ES modules) loads all dependencies first before executing when loading a module.
When there's a circular reference, modules reference other modules before they are fully initialized, resulting in referencing values in an uninitialized state.
This can cause undefined or unexpected values to be returned.
Memory LeakCircular references can interfere with garbage collection.
Especially circular references between objects can cause memory leaks.
Garbage collection runs when the reference count of a variable becomes 0, but with circular references, it can never become 0.
Difficulty in Code MaintenanceCircular references make code difficult to understand and maintain.
Especially when circular references occur, dependencies between modules become complex, making the code difficult to understand.
This hinders code reusability and extensibility.
Difficulty in TestingCircular references make testing difficult.
Especially when dependencies between modules become complex, writing test code becomes difficult.
This was also the problem I experienced during the Storybook writing process mentioned earlier.

πŸ€” Examples and Problems of Circular References

Let's look at the circular dependency problem through an example using simple React components.

TSX

References occur like moduleA β†’ moduleB β†’ moduleA β†’ moduleB β†’ moduleA β†’ moduleB β†’ moduleA β†’ ...

And this can cause the following error:

Markdown

This is because an infinite loop occurred and the call stack was exceeded.

πŸ€” How to Detect Circular Dependency Problems

Actually, the best approach is to consciously check every time while progressing.

However, doing so causes productivity to drop significantly.

Considering whether each one will occur or not every time... we have too many problems to solve and things to pay attention to.

So let's learn about tools and methods to detect these problems.

πŸ“ Detecting Circular References Using ESLint

You can detect circular references using ESLint.

Using the eslint-plugin-import plugin, you can detect circular references.

Bash

Then add the following configuration to the .eslintrc.js file.

JavaScript

With this configuration, ESLint will detect circular references and raise an error.

Actually, when I found and used ESLint on the internet, I always downloaded and used the import module... but I just learned this time that it's used for this purpose...

As always, if you don't properly understand something, you'll eventually pay the price...

πŸ“ Detecting Circular References Using TypeScript

You can detect circular references using TypeScript.

TypeScript basically detects circular references and raises an error.

πŸ“ Using Circular Reference Visualization Tools

Using tools like madge, you can visually display code dependencies to easily detect circular references.

Installation

Bash

Detecting Circular References

Then execute the following command to visually check the dependency graph.

Bash

src/ is the path to the source directory to analyze.

This will output files and paths that have circular references.

Visualizing the Dependency Graph

Bash

graph.svg is the name of the graph file to be generated.

src/ is the path to the source directory to analyze.

This will generate a graph.svg file to visually check the dependency graph.

πŸ€” How to Solve Circular Dependency Problems

To solve circular references, you need to restructure dependencies between modules or introduce an intermediate layer to separate dependencies.

Below are common solutions.

SolutionDescription
Apply Dependency Inversion Principle (DIP)The Dependency Inversion Principle (DIP) is a principle that makes high-level modules not depend on low-level modules, but depend on abstracted interfaces.
This can reduce direct dependencies between modules.
Apply Dependency Injection (DI)Dependency Injection (DI) is a design pattern that has dependencies injected from outside.
This allows module dependencies to be injected from outside, preventing circular references.
Separate into Common ModulesCommon functionality that two or more modules don't need to reference each other for can be separated into a separate module to remove dependencies.
Use Dynamic ImportIf circular references are essential, you can avoid circular references at initialization time by using dynamic imports to load modules at runtime.
Redesign Dependency StructureThis is the fundamental solution.
Redesign the dependency structure between modules so that circular references don't occur.

🧸 Real-World Case

As mentioned earlier, this is a problem I actually experienced while writing Storybook stories.

The resolution process can be referenced below.

Solving Circular Reference Error During Storybook Writing: Dropdown Component Case Analysis

πŸ“š Summary

  • Circular reference means a situation where two or more modules reference each other directly or indirectly.
  • Circular references cause various problems such as module initialization problems, memory leaks, difficulty in code maintenance, and difficulty in testing.
  • To detect circular references, you can use tools like ESLint, TypeScript, and madge.
  • To solve circular references, you can use methods such as Dependency Inversion Principle (DIP), Dependency Injection (DI), separating into common modules, dynamic import, and redesigning dependency structure.

We've learned about circular references.

One of the things I often heard at NAVER Boostcamp and in my major classes was the Single Responsibility Principle (SRP).

Even without going that far, the message was that it's better to keep dependencies as low as possible.

This was from the perspective that many unexpected errors can occur and maintenance becomes difficult.

While learning about circular references this time... I could feel that more diverse meanings were implied.

I thought I knew something... but I still feel like I have a lot to learn... I need to work harder...