Loadable-components: Usage with HOC loading

Created on 19 Oct 2017  ·  24Comments  ·  Source: gregberge/loadable-components

Just a tip on how to use this library together with a HOC-loader like redux-connect:

// Your wrapped component
const MyAsyncComponent = loadable(() => import('./MyAsyncComponent/MyAsyncComponent'))

// Utility to extract the wrapped component if loadable-HOC will conflict with your other HOCs
const loadableWrapper = (cb, LoadableComponent) => {
  LoadableComponent.load().then((Comp) => {
    cb(null, Comp)
  }).catch((error) => {
    console.log('Error loading', error)
  })
}

<Route path='/some-path' getComponent={(nextState, cb) => loadableWrapper(cb, MyAsyncComponent)} />

Most helpful comment

Later discovered that for SSR intial render we need to extract loadable-id and set a loadable key on the loaded component. If not the component would not be included in the script-tag, and you will have a flicker with nothing on the screen after intial SSR render while client-side is taking over. I use Preact and Preact-compat in production and it works fine now.

// Your wrapped component
const MyAsyncComponent = loadable(() => import('./MyAsyncComponent/MyAsyncComponent'))

// In your router-file
import { LOADABLE } from 'loadable-components/constants'

// Utility to extract the wrapped component if loadable-HOC will conflict with your other HOCs
const loadableWrapper = (cb, LoadableComponent) => {
  LoadableComponent.load().then((Comp) => {
    // Add LOADABLE-key and loadable-componentId to the component you send back to react-router (v3)
    Comp[LOADABLE] = () => LoadableComponent
    Comp.componentId = LoadableComponent.componentId
    cb(null, Comp)
  }).catch((error) => {
    console.log('Error loading', error)
  })
}

<Route path='/some-path' getComponent={(nextState, cb) => loadableWrapper(cb, MyAsyncComponent)} />

All 24 comments

Later discovered that for SSR intial render we need to extract loadable-id and set a loadable key on the loaded component. If not the component would not be included in the script-tag, and you will have a flicker with nothing on the screen after intial SSR render while client-side is taking over. I use Preact and Preact-compat in production and it works fine now.

// Your wrapped component
const MyAsyncComponent = loadable(() => import('./MyAsyncComponent/MyAsyncComponent'))

// In your router-file
import { LOADABLE } from 'loadable-components/constants'

// Utility to extract the wrapped component if loadable-HOC will conflict with your other HOCs
const loadableWrapper = (cb, LoadableComponent) => {
  LoadableComponent.load().then((Comp) => {
    // Add LOADABLE-key and loadable-componentId to the component you send back to react-router (v3)
    Comp[LOADABLE] = () => LoadableComponent
    Comp.componentId = LoadableComponent.componentId
    cb(null, Comp)
  }).catch((error) => {
    console.log('Error loading', error)
  })
}

<Route path='/some-path' getComponent={(nextState, cb) => loadableWrapper(cb, MyAsyncComponent)} />

Thanks for this :)

@gerhardsletten you're rock! Do you work around with react loadable and this issue? I can't repeat the same behaviour.

The above code is with react-router v3, but with react-router v4 (and redux-connect) combining both the async Hoc and make the container lazyload is not possible. You need to re-organize your code having the Hoc above, and inside it lazyload the View with all heavy dendencies, like this:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { asyncConnect } from 'redux-connect'
import loadable from '@loadable/component'
import { isLoaded as isPageLoaded, load as loadPage } from 'redux/modules/page'

const PageView = loadable(() =>
  import(/* webpackChunkName: "PageView" */'./PageView')
)

@asyncConnect([
  {
    promise: ({ store: { dispatch, getState }, location: { pathname } }) => {
      const promises = []
      if (!isPageLoaded(getState(), pathname)) {
        promises.push(dispatch(loadPage(pathname)))
      }
      return Promise.all(promises)
    }
  }
])
@connect(
  state => ({
    page: state.page.data
  })
)
export default class PageViewContainer extends Component {
  render() {
    return <PageView {...this.props} />
  }
}

Hi.
Is this still the right way to work with asyncConnect'ed components? It seems like the load() method is not available on the loadable component.

Hi @jjblumenfeld

I just updated my latest commend from 9 august with the new scoped package-name for loadable.

Mark that the load-method is from my own projects redux-ducks modules, it return a promise used within @asyncConnect to reflect when the pages content is loaded

...
import { isLoaded as isPageLoaded, load as loadPage } from 'redux/modules/page'

Thanks. I am trying that now, it is not quite working yet for me. I am using react-router v3.2.1 and redux-connect v6.0.0, so I'm not sure if I should be using something more like the first example or the second one?

redux-connect just published v9 that supports [email protected] (new context api), maybe you need to update. Is it redux-connect and loading everything on server that is the problem, or is it the code-splitting that does not work?

I was able to get code-splitting working, but loading everything serverside
is not working for me.

On Tue, Mar 12, 2019 at 5:30 AM Gerhard Sletten notifications@github.com
wrote:

redux-connect just published v9 that supports [email protected] (new
context api), maybe you need to update. Is it redux-connect and loading
everything on server that is the problem, or is it the code-splitting that
does not work?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/smooth-code/loadable-components/issues/15#issuecomment-471920281,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACAh5jJKRPmRobeqSbXwIlLV2UgXdNKCks5vV3OfgaJpZM4P-5bT
.

Its hard to tell you what you do wrong without seeing some code, please provide that and I will look into it. But in the server, its important to call redux-connects loadOnServer before ChunkExtractor from @loadable/server, and you can only do code-splitting below asyncConnect

Thanks again for your patient replies!
I found my issue. When I was re-factoring my code I had left some of the loadable/code-splitting code above an asyncConnect. I have re-factored it to put the asyncConnect around a wrapper which contains the loadable component as you had in your second example and it all works nicely now.

Great!

Hi @jjblumenfeld

I just updated my latest commend from 9 august with the new scoped package-name for loadable.

Mark that the load-method is from my own projects redux-ducks modules, it return a promise used within @asyncConnect to reflect when the pages content is loaded

...
import { isLoaded as isPageLoaded, load as loadPage } from 'redux/modules/page'

Hey @gerhardsletten, can you please share what is inside your import { isLoaded as isPageLoaded, load as loadPage } from 'redux/modules/page'? I wan't to see how do you load the component.

I'm trying to solve Did not expect server HTML to contain a <div> in <div>. this problem for about a week already and no success. On server, I did everything correctly, put ChunkExtractor after loadOnServer.
On the client, I wrapped hydration with loadableReady, but whenever I add loadable to any route, it starts showing me the error above.

Here is the screenshot with the populated HTML from server
Screen Shot 2020-09-27 at 11 59 49 PM

and this one is after hydration
Screen Shot 2020-09-28 at 12 00 57 AM

this is our home page route, wrapped to our asyncConnectWithModifications which is same as asyncConnect
Screen Shot 2020-09-28 at 12 04 15 AM

@gloompi Taking a look at your screenshot, and I think the connected Component needs to be a regular react-component and not a "loadable"-component, so if you add

const Home = loadable(() => import('./Home'))
// Insert this
const HomeContainer = (props) => {
  return <Home {...props} />
}

And at the end you pass this HomeContainer:

)(HomeContainer)

@gerhardsletten I have tried this approach as well, but it didn't work. I still think that it's related to async load somehow, can you share what were doing inside the redux? Especially the loadPage action?

@gloompi Could you extract this into an example and share this on github? Getting it right with ssr also involves your babel-config, webpack setup and both your server.js and client.js entrypoint. Most likely you have something one places that cases this problem.

@gerhardsletten sure, I actually have opened an issue here https://github.com/gregberge/loadable-components/issues/633.
And there you can find all the examples with configs.

@gerhardsletten if it's not enough, I can try to create a repo with similar configs

@gloompi It would be great if you could extract a runable example

@gerhardsletten hey, glad to inform you that the new version of loadable/components solved my issue. Thank you for helping, really appreciate it❤️❤️❤️

@gloompi Great, all good debug-sessions starts with npm update ;-)

Was this page helpful?
0 / 5 - 0 ratings