Search code examples
javascriptreactjsecmascript-6commonjs

How to handle React nested component circular dependency? (using es6 classes)


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 : )


Solution

  • 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} />
      })
      …
    }