16

I am using React with Babel (es6). I have a problem to solve a circular dependency between my components.

I am building a menu where we have:

  • ItemFactory
  • ItemFolder
  • ItemGeneric
  • MoreTypesOfItems

ItemFactory simply returns any of the other components, based on the props passed.

The ItemFolder needs to be able to reuse the ItemFactory, to build nested menus items.

ItemFactory component, itemFactory.js (simplified):

 // I need ItemFolder import ItemFolder from './itemFolder' // import all different items too ... function ItemFactory (props) { switch (props.type) { case 'link': return <ItemLink {...props} /> case 'blank': return <ItemBlank {...props} /> case 'folder': return <ItemFolder {...props} /> default: return <ItemGeneric {...props} /> } } modules.exports = ItemFactory 

ItemFolder Component, itemFolder.js (simplified):

 // I need the itemFactory import ItemFactory from './itemFactory' ... Items = props.items.map(item => { return <ItemFactory {...item} /> }) module.exports = ItemFolder 

As you can see both are requiring each other. This causes some circular dependency problem where I get an empty object.

Any help appreciated : )

1
  • 2
    Don't use module.exports = . Use an ES6 export. Commented Feb 22, 2016 at 17:58

2 Answers 2

12

In a dependency circle, the imported items will be resolved to the exported binding which is not yet initialised. This should not cause any problems if you are not immediately using them (like calling the functions).

Your problem might be the use of CommonJs export syntax. The following should work:

// itemFactory.js import ItemFolder from './itemFolder' … export default function ItemFactory(props) { switch (props.type) { case 'link': return <ItemLink {...props} /> case 'blank': return <ItemBlank {...props} /> case 'folder': return <ItemFolder {...props} /> default: return <ItemGeneric {...props} /> } } 

// itemFolder.js import ItemFactory from './itemFactory' … export default function ItemFolder(props) { let items = props.items.map(item => { return <ItemFactory {...item} /> }) … } 
Sign up to request clarification or add additional context in comments.

3 Comments

This solved the problem however I'd like to comment the following. The reason I had module.exports instead of export / export default is because updating to babel 6 changed the way the default export works. After reading some articles like this one, I understand es6 modules better: medium.com/@kentcdodds/… Finally I've updated my exports, solving the circular dependency problem, and I had to require my menu using the defaultproperty require('./appMenu').default at another point in my app.
Hi @Bergi, I also faced same issue, I applied your solution, still its not working. may be eslint rules causing that issue, can you tell me how to disable import/no-cycle rule in eslint
9

One way is to have the individual components inject themselves into the ItemFactory. This has the advantage of making the factory more easily extended with new types:

const components = {}; export function registerComponent(name, Component) { components[name] = Component; }; export default function ItemFactory(props) { const C = components[props.type]; return <C {...props} />; } // ItemFolder.js import {registerComponent}, ItemFactory from 'itemFactory'; export default class ItemFolder { ... }; registerComponent("folder", ItemFolder); 

1 Comment

I chose the other answer because it was simpler in my case but definitively useful answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.