DeloitteAU / react-habitat

āš›ļø šŸ›… A React DOM Bootstrapper designed to harmonise a hybrid 'CMS + React' application.
Other
262 stars 42 forks source link

ERROR: RHW01 Cannot resolve component "XXX" for element. http://tinyurl.com/jxryd3s#rhw01 TypeError: Cannot read property 'insertBefore' of null #41

Closed olafur164 closed 5 years ago

olafur164 commented 5 years ago

I think it's a bit related to https://github.com/DeloitteDigitalAPAC/react-habitat/issues/37

Each of our react components are being rendered correctly and work fine but it's quite annoying to see these errors šŸ˜¢

CMS: Umbraco HTML Example:

<div data-component="RSolutionSection"
                    data-prop-background-color="@pageColorGradient"
                    data-prop-align="center left"
                    data-prop-section-title="@title"
                    data-prop-cards="@cardsArray.ToString()"
                    data-prop-translations="@translations.ToString()"
                    data-prop-link-text="@link.Caption"
                    data-prop-link-url="@link.Url"
                    data-prop-src="@desktopImage"
                    data-prop-src-tablet="@tabletImage"
                    data-prop-src-mobile="@mobileImage"
                    data-prop-section-id="@sectionID">
...
</div>

ComponentRegistrations (Small insight in our registration code):

class ComponentContainer extends ReactHabitat.Bootstrapper {
  constructor() {
    super()
    const containerBuilder: ReactHabitat.IContainerBuilder = new ReactHabitat.ContainerBuilder()
    const store = configureStore()
    containerBuilder.factory = new ReduxDomFactory(store)

    containerBuilder
      .registerAsync(System.import('./components/Interfaces/SolutionSectionInterface'))
      .as('RSolutionSection')
      .withOptions({ className: 'section-component-wrapper' })

    this.setContainer(containerBuilder.build())
  }
}

const instance = new ComponentContainer()

declare global {
  // tslint:disable-next-line:interface-name
  interface Window {
    updateHabitat?: (Node?: any) => void
    Barba: any
  }
}

window.updateHabitat = instance.update.bind(instance)

// Barba should be global.
window.Barba = Barba
Barba.Dispatcher.on('newPageReady', (currentStatus, oldStatus, container) => {
  if (window.updateHabitat) {
    if (Object.keys(oldStatus).length > 0) {
      window.updateHabitat()
    }
  }
})

Barba.Dispatcher.on('transitionCompleted', () => {
  const event = document.createEvent('Event')
  event.initEvent('scroll', false, true)
  window.dispatchEvent(event)
})

const loadingElement = document.getElementsByClassName('loading')[0]
const loadingFromPage = document.getElementById('loading-from-page')
const loadingToPage = document.getElementById('loading-to-page')
const loadingLogo = document.getElementById('loading-logo')
const body = document.querySelector('body')
const html = document.querySelector('html')

// Initialize Barba.js
// Please note, the DOM should be ready
const HideShowTransition = Barba.BaseTransition.extend({
  start() {
    this.oldContainer.classList.add('is-transitioning', 'old-container')

    if (
      loadingFromPage !== null &&
      loadingToPage !== null &&
      loadingElement !== null &&
      loadingLogo !== null
    ) {
      loadingElement.classList.add('enter')
      loadingFromPage.classList.add('enter')
      loadingLogo.classList.add('enter')
    }

    this.newContainerLoading.then(this.finishing.bind(this))
  },

  finishing() {
    this.newContainer.classList.add('is-transitioning', 'new-container')
    setTimeout(() => {
      this.finish()
    }, 1000)
  },

  finish() {
    if (
      loadingFromPage !== null &&
      loadingToPage !== null &&
      loadingElement !== null &&
      loadingLogo !== null
    ) {
      this.newContainerLoading
        .then(window.scrollTo(0, 0))
        .then(loadingToPage.classList.add('enter'))
        .then(this.newContainer.classList.remove('new-container'))
        .then(
          setTimeout(() => {
            loadingToPage.classList.add('exit')
            loadingElement.classList.add('exit')
            loadingFromPage.classList.add('exit')
            loadingLogo.classList.add('exit')
          }, 500)
        )
        .then(
          setTimeout(() => {
            loadingToPage.classList.remove('exit', 'enter')
            loadingElement.classList.remove('exit', 'enter')
            loadingFromPage.classList.remove('exit', 'enter')
            loadingLogo.classList.remove('exit', 'enter')
            this.newContainer.classList.remove('is-transitioning')
          }, 1000)
        )
        .then(() => {
          const language = getLanguageFromUrl()

          if (html && language !== '') {
            html.setAttribute('lang', language)
          }
        })
    }

    this.done()
  },
})
Barba.Pjax.getTransition = () => {
  return HideShowTransition
}
Barba.Utils.xhrTimeout = 15000
Barba.Pjax.start()

// Un-hide body.
if (body) {
  body.className = ''
}

export default instance
olafur164 commented 5 years ago

Current version of React-Habitat is "react-habitat": "^0.5.0",

olafur164 commented 5 years ago

After some research I believe this is somehow related to the fact that we have nested components were we call in the parent component on componentDidMount()

if (window.updateHabitat) {
      window.updateHabitat()
    }
olafur164 commented 5 years ago

Resolved this by updating to the latest stable version of react habitat and instead of components .registerAsync(System.import('./components/Interfaces/ProductHeroSectionInterface')) I did .registerAsync(() => System.import('./components/Interfaces/ProductHeroSectionInterface'))

jennasalau commented 5 years ago

Hi @olafur164 thanks for taking the time to research and document.

Glad you got it resolved.