Giter VIP home page Giter VIP logo

loadable-components's Introduction

loadable-components

React code splitting made easy. Reduce your bundle size without stress ✂️✨.

License npm package npm downloads Build Status Code style Dependencies DevDependencies Small size

npm install @loadable/component

See the documentation at loadable-components.com for more information about using Loadable Components!

Quicklinks to some of the most-visited pages:

Example

import loadable from '@loadable/component'

const OtherComponent = loadable(() => import('./OtherComponent'))

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  )
}

Supporting Loadable Components

Loadable Components is an MIT-licensed open source project. It's an independent project with ongoing development made possible thanks to the support of these awesome backers. If you'd like to join them, please consider:

License

Licensed under the MIT License, Copyright © 2017-present Greg Bergé.

See LICENSE for more information.

loadable-components's People

Contributors

7rulnik avatar abramstyle avatar aganglada avatar ambar avatar apostolos avatar ashudson23 avatar bertho-zero avatar brandon-pereira avatar brkalow avatar csabapalfi avatar damassi avatar danielruf avatar dantehemerson avatar dependabot[bot] avatar fivethreeo avatar gregberge avatar hedgepigdaniel avatar jas0ncn avatar justin808 avatar kaysonwu avatar lucasterra avatar malstoun avatar markwoodward23 avatar oreqizer avatar patrickhaug avatar philibea avatar salzhrani avatar simek avatar thekashey avatar threehams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

loadable-components's Issues

Error Handling not working

the loadableComponent does not resolve the errorComponent listed when the imported component throws an error or fails to resolve. it is just stuck in the loadingComponent.

image

here is the code in question

const config = {
  LoadingComponent: () => <Loading />,
  ErrorComponent: () => <Error />
};

function setPageRoute(Name) {
  return loadable(() => import(`../Pages${Name}`), {
    ...config
  });
}

const Home = setPageRoute('/Home');

Add TypeScript Support

I find ``loadable-components``` very good, but no provide TypeScript support
You can provide TypeScript support
So much the better 😃

MODULE_NOT_FOUND when running in development mode with SSR & webpack-hot-server-middleware

Hi there,

I have got the following error during the server side rendering in development mode.

{ Error: Cannot find module './vendors~account~general.chunk.js'
    at Function.Module._resolveFilename (module.js:536:15)
    at Function.Module._load (module.js:466:25)
    at Module.require (module.js:579:17)
    at require (internal/module.js:11:18)
    at Function.requireEnsure [as e] (/srv/project/public/server/main.js:49:25)
    at loadable_components__WEBPACK_IMPORTED_MODULE_0___default.modules (webpack:///./src/components/index.js?:7:68)
    at Function.load (/srv/project/node_modules/loadable-components/src/loadable.js:26:44)
    at eval (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:183:28)
    at walkTree (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:113:13)
    at walkTree (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:133:11)
    at walkTree (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:133:11)
    at walkTree (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:133:11)
    at eval (webpack:///./node_modules/loadable-components/dist/loadable-components.server.cjs.js?:158:13)
    at forEachSingleChild (/srv/project/node_modules/react/cjs/react.development.js:858:8)
    at traverseAllChildrenImpl (/srv/project/node_modules/react/cjs/react.development.js:762:5)
    at traverseAllChildrenImpl (/srv/project/node_modules/react/cjs/react.development.js:778:23) code: 'MODULE_NOT_FOUND' }

It seems to me that getLoadableState unable to get the chunk file in webpack-dev-middleware or has problem with the webpack-hot-server-middleware when code splitting is enabled in the server build.(?)

I am currently using a workaround by disabling the code splitting in the server build using limit-chunk-count-plugin, i.e.

new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })

Module not found: Error: Can't resolve 'loadable-components/snap'

I'm having an issue loading getState from loadable-components/snap. We're using yarn 1.3.2, node 8.9.3, and loadable-components 1.1.0.

Using import loadable from 'loadable-components'; works just fine in our project, as does attempting to import the loadable-components/server, but if we try to use the snippet for working with snapshots as listed in the Readme:

import { getState } from 'loadable-components/snap'

// Set up for react-snap.
window.snapSaveState = () => getState()

We get the following error:

Module not found: Error: Can't resolve 'loadable-components/snap' in '/app/app'
 @ ./app/index.js 7:0-52
 @ multi (webpack)-dev-server/client?http://0.0.0.0:8080 webpack/hot/dev-server ./app/index.js

Are we importing wrong? Any thoughts?

Thank you for this library!

Hi, I just want to thank you for this awesome library, this is actually the only one lib that worked perfectly with my https://github.com/antonybudianto/cra-universal which used react 16 and renderToNodeStream.
I tried many other solutions but they didn't work well despite their setup being very complicated.
I'll mention and recommend this repo in my repo wiki for Code-splitting solution

Keep it up! 👍

Comparison with other libraries

Hi @neoziro @vvo! This library looks great - thank you for open sourcing this!

Its probably on your list, but if not, do consider providing a more indepth comparison with react-loadable, and react-async-components.

Personally, I am curious to understand if SSR is the main difference, or are there other differences, as well, as how you approach SSR differently from those libraries.

It'd be super helpful to folks, like me, trying to pick a library!

Thank you!

warnForDeletedHydratableElement: Warning: Did not expect server HTML to contain a <h1> in <div>

Hello, this is an awesome solution. Thanks a lot for this.

I've ran into an issue that I cannot solve and I spent a good 2 -3 hours trying to figure it out.
I'm using the full SSR solution with getLoadableState server side and loadComponents before using ReactDOM.hydrate on the client.
I cannot get rid of this warning:

Warning: Did not expect server HTML to contain a h1 in div
It comes from warnForDeletedHydratableElement function in React.

the h1 is the root element in the code splitted bundles.

For whatever reason when I hydrate the markup is not matching, even though I'm using loadComponents() before I hydrate. The loadComponents promise is resolving and this warning still occurs.

When I remove the code splitting this warning is gone so there's something going on that I can't figure out.

Any ideas would be much appreciated.

The code can be found here: https://github.com/nreoch25/mern-boilerplate
Any help would be very much appreciated.

thanks

Warning when using react-hot-loader@next

I was able to setup loadable-components with react-hot-loader@next successfully with the link you have provided in issue #43.
Both code splitting and hmr are working as expected but it also displays some warnings in the console which I don't have when I only use react-hot-loader without loadable-components.
But as it still works, it is probably not a high priority issue but it would be good if the warning can get resolved.

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.

Please check the code for the EmptyComponent component.

On the initial load, the warning appears only with EmptyComponent (which I think comes from loadable-components) but when I change the route, the same warning also gets spammed into the console multiple times with my own components.

Stacktrace of the warning:

    printWarning | @ | warning.js?6327:33
  | warning | @ | warning.js?6327:57
  | warnAboutUpdateOnUnmounted | @ | react-dom.development.js?cada:9766
  | scheduleWorkImpl | @ | react-dom.development.js?cada:10737
  | scheduleWork | @ | react-dom.development.js?cada:10689
  | enqueueForceUpdate | @ | react-dom.development.js?cada:6250
  | Component.forceUpdate | @ | react.development.js?99ee:255
  | updateInstance | @ | react-hot-loader.development.js?280f:35
  | (anonymous) | @ | react-hot-loader.dev…opment.js?280f:1040
  | flushScheduledUpdates | @ | react-hot-loader.dev…opment.js?280f:1039
  | setTimeout (async) |   |  
  | scheduleInstanceUpdate | @ | react-hot-loader.dev…opment.js?280f:1047
  | (anonymous) | @ | react-hot-loader.dev…opment.js?280f:1115
  | hotReplacementRender | @ | react-hot-loader.dev…opment.js?280f:1057
  | hotReplacementRender$1 | @ | react-hot-loader.dev…opment.js?280f:1124
  | reconcileHotReplacement | @ | react-hot-loader.dev…opment.js?280f:1133
  | renderReconciler | @ | react-hot-loader.dev…opment.js?280f:1143
  | proxiedRender | @ | react-hot-loader.development.js?280f:514
  | finishClassComponent | @ | react-dom.development.js?cada:7873
  | updateClassComponent | @ | react-dom.development.js?cada:7850
  | beginWork | @ | react-dom.development.js?cada:8225
  | performUnitOfWork | @ | react-dom.development.js?cada:10224
  | workLoop | @ | react-dom.development.js?cada:10288
  | callCallback | @ | react-dom.development.js?cada:542
  | invokeGuardedCallbackDev | @ | react-dom.development.js?cada:581
  | invokeGuardedCallback | @ | react-dom.development.js?cada:438
  | renderRoot | @ | react-dom.development.js?cada:10366
  | performWorkOnRoot | @ | react-dom.development.js?cada:11014
  | performWork | @ | react-dom.development.js?cada:10967
  | requestWork | @ | react-dom.development.js?cada:10878
  | scheduleWorkImpl | @ | react-dom.development.js?cada:10732
  | scheduleWork | @ | react-dom.development.js?cada:10689
  | enqueueSetState | @ | react-dom.development.js?cada:6212
  | Component.setState | @ | react.development.js?99ee:237
  | safeSetState | @ | loadable-components.es.js?5dbf:220
  | (anonymous) | @ | loadable-components.es.js?5dbf:204
  | Promise resolved (async) |  

Please tell me when you need any additional information.

Document is not defined with SSR and Redux

I have this code

const store = createStore()

  const promises = matchRoutes(routes, req.url).map(({ route, match }) => {
    const { fetchData } = route.component
    return fetchData instanceof Function ? store.dispatch(fetchData(match)) : Promise.resolve(null)
  })

  Promise.all(promises).then(() => {
    const context = {}
    const appWithRouter = (
      <Provider store={store}>
        <StaticRouter location={req.url} context={context}>
          {renderRoutes(routes)}
        </StaticRouter>
      </Provider>
    )
  
    if (context.url) {
      return res.redirect(context.url)
    }

    getLoadableState(appWithRouter).then(loadableState => {
      const html = ReactDOMServer.renderToString(appWithRouter)
      renderApp(res, store, {}, req.url, html, loadableState)
    }).catch(err => {
      return res.status(500).end('Internal Server Error')
    })
  })

Which works fine when I'm not dispatching anything to redux store.

so in this function here

const promises = matchRoutes(routes, req.url).map(({ route, match }) => {
    const { fetchData } = route.component
    return fetchData instanceof Function ? store.dispatch(fetchData(match)) : Promise.resolve(null)
  })

if my component doesn't fetch any data, then al works fine, but if it doesn fetch data, then the getLoadableState fails returning this:

ReferenceError: document is not defined
    at Parser.createDocument (/home/davidec/Documents/sites/muso/webapp/node_modules/interweave/lib/Parser.js:217:41)
    at new Parser (/home/davidec/Documents/sites/muso/webapp/node_modules/interweave/lib/Parser.js:69:21)
    at Interweave.parseMarkup (/home/davidec/Documents/sites/muso/webapp/node_modules/interweave/lib/Interweave.js:100:20)
    at Interweave.render (/home/davidec/Documents/sites/muso/webapp/node_modules/interweave/lib/Interweave.js:133:14)
    at walkTree (/home/davidec/Documents/sites/muso/webapp/node_modules/loadable-components/src/server/index.js:83:26)
    at /home/davidec/Documents/sites/muso/webapp/node_modules/loadable-components/dist/loadable-components.server.cjs.js:158:13
    at forEachSingleChild (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:849:8)
    at traverseAllChildrenImpl (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:753:5)
    at traverseAllChildrenImpl (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:769:23)
    at traverseAllChildren (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:824:10)
    at Object.forEachChildren [as forEach] (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:869:3)
    at walkTree (/home/davidec/Documents/sites/muso/webapp/node_modules/loadable-components/src/server/index.js:120:24)
    at walkTree (/home/davidec/Documents/sites/muso/webapp/node_modules/loadable-components/dist/loadable-components.server.cjs.js:133:11)
    at /home/davidec/Documents/sites/muso/webapp/node_modules/loadable-components/dist/loadable-components.server.cjs.js:158:13
    at forEachSingleChild (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:849:8)
    at traverseAllChildrenImpl (/home/davidec/Documents/sites/muso/webapp/node_modules/react/cjs/react.development.js:753:5)

I can't figure out wha tthe problem is...

Delay

Is there a way you could do a to show the component right away if it's there, but delay showing the loading spinner only for at least some time if still loading?

Loaded all chunks in development mode

Hello, I'm use webpack 3 and loadable-components
In development mode when I'm trying open page of application I see what all async chunks loaded immediately. Why?
image

I'm use routing based code-splitting

export default (
  <Switch>
    <Route path='/' exact component={userIsNotAuthenticated(IndexContainer)} />
    <Route path='/main/:anchorSectionId' component={IndexContainer} />
    <Route exact path='/:type(oge|ege)' component={EgeOgeContainer} />
    <Route exact path='/olymp-courses' component={OlympCoursesContainer} />
    ...
  </Switch>
)

In production build I see only what I need for currently route
image

loadComponents null

Hi, I just upgraded to 1.0.0

and I got the following error:

Uncaught TypeError: Cannot read property 'then' of null
    at Object../src/index.js (index.js:13)
    at __webpack_require__ (bootstrap 9fe4b7230cd8077e37a9:707)
    at fn (bootstrap 9fe4b7230cd8077e37a9:112)
    at Object.0 (registerServiceWorker.js:117)
    at __webpack_require__ (bootstrap 9fe4b7230cd8077e37a9:707)
    at ./node_modules/@firebase/app/dist/esm/index.js.Object.defineProperty.value (bootstrap 9fe4b7230cd8077e37a9:805)
    at bundle.js:809

The offending line:

loadComponents().then(() => {

Please help, thank you

Error: loadable-components state not found

I commented on a closed issue and I thought you may not notice it so I am repeating myself here

Not sure if this change was necessary
if (!state || !state.children)

You may not have a component that needs to be lazy loaded

Attach static methods of wrapped component to loadable component

Attach static methods of wrapped component to loadable component

c1.mjs

import * as React from 'react'
const createElement = React.default.createElement
const Component = React.default.Component

export default class c1 extends Component {
  constructor( props ){
    super( props )
  }
  render(){
    return createElement( 'p', {}, 'hello' )
  }
  static async preloadDataState( parameters ){
    debugger
  }
}

main.mjs

import * as React from 'react'
const createElement = React.default.createElement

import * as LoadableComponents from 'loadable-components'
const loadable = LoadableComponents.default.default

import * as LoadableComponentsServer from 'loadable-components/server'
const getLoadableState = LoadableComponentsServer.default.getLoadableState

const c1 = loadable( () => import( /* webpackChunkName: "main.c1" */ './c1.mjs' ), {
  modules: [ './c1.mjs' ],
} )

const composition = createElement( c1 )

getLoadableState( composition ).then( ls => {
  console.log( c1.Component.preloadDataState )
} )

output:

[AsyncFunction: preloadDataState]

Suggestion:
attach static methods or fields directly to loadable component ( c1 ):
preloadDataState method available by c1.preloadDataState instead of c1.Component.preloadDataState

UnhandledPromiseRejectionWarning on node v9.5.0

(node:68523) UnhandledPromiseRejectionWarning: TypeError: getComponent(...).then is not a function
at Function.load (/Users/.../node_modules/loadable-components/dist/loadable-components.cjs.js:191:59)

calling ReactDOM.hydrate

calling ReactDOM.hydrate causes an error:
Did not expect server HTML to contain a <div> in <div>.

getLoadableState return empty object on development

@neoziro I'm using this library with SSR + [email protected]. And I found the getLoadableState return empty object like as window.__LOADABLE_STATE__ = {}; on development but on production it return the correct path of async component like as window.__LOADABLE_STATE__={children:[{id:"./Home"}]}. Does this behavior meet expectation? It can be reproduce on the last commit of here

window[LOADABLE_STATE] is not working

I'm trying to use this lubrary with SSR, but I keep receiving this error loadable-components state not found. You have a problem server-side. Please verify that you have calledloadableState.getScriptTag() server-side. I dive into the module and I put a console.log(window) in the loadComponents() function, the result contains this:
image

but if I do console.log(window[LOADABLE_STATE]); is returning undefined.

Do you have any clue about what is happening?

Add support for preload meta tags

Great work on this library! Seems like an elegant solution to a very hairy problem space. I'd like to add support for rendering preload meta tags on the server to shorten the request waterfall.

Something like chunk-manifest-webpack-plugincould probably be used to achieve this, but I thought I'd ask if this is something you have already considered before to trying to build something myself.

I get that this might be outside the scope of this module, buy having a recommended/suggested way of doing this would be good.

Cheers

ssr not working with webpack 4.0.0-beta1

export const Layout = loadable( () => import( / webpackChunkName: "main.layout" / './components/layouts/container.mjs' ) )

first error:
loadable-components: modules entry is missing, your are probably missing loadable-components/babel

then I set in webpack server config:

module: {
    rules: [
      {
        test: /\.(js|mjs)$/,
        use: [
          { 
            loader: 'babel-loader', 
            options: { plugins: [  'loadable-components/babel', 'dynamic-import-node' ] }
          }
        ],
        exclude: /node_modules/
      }
    ]
  },

then another error:
Error: Cannot find module './components/layouts/container.mjs'

"devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-dynamic-import-node": "^1.2.0",
    "webpack": "^4.0.0-beta.1",
    "webpack-cli": "^2.0.4",
    "webpack-node-externals": "^1.6.0"
  },
"dependencies": {
    "loadable-components": "^1.1.1",
    "prop-types": "^15.6.0",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-redux": "^5.0.6",
    "redux": "^4.0.0-beta.1",
    "styled-components": "^3.1.6"
  }

but all works with webpack 3.10.0

getLoadableState)(...).then is not a function

First off, great work! I love the simplicity of this project.

Expected behavior

getLoadableState() should return a promise.

Actual behavior

getLoadableState() returns an instance of DeferredState if no queries are found.

Steps to reproduce

  1. From the root of an empty directory: yarn add loadable-components.
  2. Open node_modules/loadable-components/server/index.js.

getLoadableState contains the following line:
if (!queries.length) return new _DeferredState2.default([]);

If I download the repo and run yarn build, this is output instead:
if (!queries.length) return Promise.resolve(new _DeferredState2.default([]));

Opening up the tarball listed on npm shows the line that isn't wrapped in Promise.resolve, so I think all that needs to be done is to update that.

Support React 16.3

  • Use new lifecycles if available
  • Check that all lifecycle does not contain any side effect

Support hot module replacement

Is there a way to support HMR on development mode? I’ve cloned your Website project but have no idea why changes have been made to components, and new chunk files pushed to browser successfully, however DOM does not reflect those changes unless I give page a total refresh?

Example of how to fetch API data for SSR

I am looking for a example of how I can achieve SSR with loadable-components that would also fetch dynamic data needed for he component of the route and have that rendered server side..

getLoadableState undefined

import { getLoadableState } from 'loadable-components';
console.log(getLoadableState) // undefined
"loadable-components": "0.2.0",

Hi, how I can fix this?

Loading state didn't changed correctlly when using custom render props

Hi, thanks for this amazing package. I have a problem when I using custom render function.

If using render props, the loading didn't change even if component is already loaded.

Here are my code:

const LoadablePosts = loadable(() => import(/* webpackChunkName: "posts" */'./Posts'), {
  render(renderProps) {
    const { Component, loading, ownProps } = renderProps;
    const { store } = ownProps;
    // 'loading' is always be 'true'
    if (loading) {
      return <div className="loading">Loading Desktop Posts...</div>;
    }

    store.replaceReducer(nextReducer());
    return (<Component {...ownProps} />);
  },
});

Usage with HOC loading

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

Uncaught TypeError: Cannot read property 'Symbol(loadable)' of undefined

Problem occurs while I work with server render and dynamic client code from webpack. Seems I have difference in module numbers when HMR recompile my client code.
Server rendering works throw babel-node and don't need webpack build.

/* eslint-env browser */
function loadComponents() {
  if (typeof window === 'undefined') {
    throw new Error('`loadComponents` must be called client-side: `window` is undefined');
  }

  var ids = window[_constants.COMPONENT_IDS] || [];
  return Promise.all(ids.map(function (id) {
    return componentTracker.get(id)[_constants.LOADABLE]().load(); // error here
  }));
}

HTML contains IDS
<script>window.__LOADABLE_COMPONENT_IDS__ = [6553];</script>

Thanks!

Error: loadable-components: modules entry is missing, your are probably missing `loadable-components/babel`

Hello. An error occurred:

Error: loadable-components: modules entry is missing, your are probably missing loadable-components/babel

An error occurs when I create an array of route objects dynamically, rather than statically.

Example, work:

const routes = [
    {
        path: '/',
        exact: true,
        name: 'Home',
        component: loadable(() => import(/* webpackChunkName: "Home"*/ './Home'))
    },
    {
        path: '/contact',
        exact: true,
        name: 'contact',
        component: loadable(() => import(/* webpackChunkName: "Contact"*/ './contact')),
    }
];
export const RootRouter = () => {
    return (
        <div>
            <Link to="/">
                home
            </Link>
            <br/>
            <Link to="/contact">
                contact
            </Link>
            <div className="container">

                <Switch>
                    {
                        routes.map(route => <Route key={'route-'+route.name} {...route} />)
                    }
                </Switch>
            </div>
        </div>)
}; 

The route constant contains this data:

[1] routes: [ { path: '/',
[1]     exact: true,
[1]     name: 'Home',
[1]     component:
[1]      { [Function: LoadableComponent]
[1]        load: [Function: load],
[1]        Component: null,
[1]        loadingPromise: null,
[1]        '@@loadable-components/loadable': [Function],
[1]        componentId: './Home' } },
[1]   { path: '/contact',
[1]     exact: true,
[1]     name: 'contact',
[1]     component:
[1]      { [Function: LoadableComponent]
[1]        load: [Function: load],
[1]        Component: null,
[1]        loadingPromise: null,
[1]        '@@loadable-components/loadable': [Function],
[1]        componentId: './contact' } } ]

And here is an example that does not work because it does not create componentId:

import React, {Component} from 'react';
import {Link, Route, Switch} from 'react-router-dom';
import * as modules from '../modules/index';
import loadable from 'loadable-components';


const MainRoute = ({component: Component, ...rest}) => {
    try {
        if (rest.hasOwnProperty('routes') && rest.routes) {
            return (
                <Switch>
                    <Route exact={true} {...rest} render={matchProps => {
                        return (
                            <Component {...matchProps} />
                        )
                    }}/>
                    {
                        rest.routes.map((item, index) => (
                            <MainRoute
                                key={`${item.path}-${index}`}
                                // exact={item.exact}
                                path={item.path}
                                component={item.component}
                                routes={item.children || null}
                            />))
                    }
                </Switch>
            )
        } else {
            return (
                <Route exact={true} {...rest} render={matchProps => {
                    return (
                        <Component {...matchProps} />
                    )
                }}/>
            )
        }

    } catch (error) {
        console.error(error);
    }

};

const createRoutes = (modulesRoutes, newRoutes, moduleName) => {

    for (let i = 0; i < modulesRoutes.length; i++) {
        if (modulesRoutes[i].hasOwnProperty('load')) {
            console.log('modulesRoutes[i].load:',modulesRoutes[i].load);
            newRoutes.push({
                exact: true,
                name: modulesRoutes[i].name,
                path: modulesRoutes[i].path || console.error(`Error: in the module ${moduleName} in one of the routes there is no property "path".`),
                component: loadable( modulesRoutes[i].load),

            })
        } else if (modulesRoutes[i].hasOwnProperty('component')) {
            newRoutes.push({
                exact: true,
                name: modulesRoutes[i].name,
                path: modulesRoutes[i].path || console.error(`Error: in the module ${moduleName} in one of the routes there is no property "path".`),
                component: modulesRoutes[i].component
            })
        } else {
            console.error(`Error: in the module ${moduleName} there is no component at 
            the address ${modulesRoutes[i].path}. Make sure that you added the "load: () => import('...')" 
            property with the component import or React component. `);
        }

        if (modulesRoutes[i].hasOwnProperty('children')) {
            newRoutes[newRoutes.length - 1].children = [...createRoutes(modulesRoutes[i].children, [], moduleName)]
        }
    }
    return newRoutes
};

let routes = [];

Object.entries(modules).map(([key, value]) => {
    if (value.hasOwnProperty('routes')) {
        routes = [...routes, ...createRoutes(value.routes, [], key)];
    } else {
        console.error(`ERROR:in the module "${key}" there is no property "routes".
        Add the property "routes" to the module "${key}" and determine at least
        one route otherwise the module will be inaccessible to users.`)
    }
});


export const RootRouter = () => (
    <div>
        <Link to="/">
            home
        </Link>
        <br/>
        <Link to="/contact">
            contact
        </Link>
        <div>
            <Switch>
                <Switch>
                    {
                        routes.map(route => <Route key={`route-${route.name}`} {...route} />)
                    }
                </Switch>
            </Switch>
        </div>
    </div>
);```

The route constant contains this data:

[ { exact: true,
[1]     name: undefined,
[1]     path: '/',
[1]     component:
[1]      { [Function: LoadableComponent]
[1]        load: [Function: load],
[1]        Component: null,
[1]        loadingPromise: null,
[1]        '@@loadable-components/loadable': [Function] } },
[1]   { exact: true,
[1]     name: undefined,
[1]     path: '/contact',
[1]     component:
[1]      { [Function: LoadableComponent]
[1]        load: [Function: load],
[1]        Component: null,
[1]        loadingPromise: null,
[1]        '@@loadable-components/loadable': [Function] } } ]

Preload scripts

Can I some hove get the path to chunk for creating preload scripts on the server side?

const chunks = ['output/chunk/path/chunkname.js', ...]
chunks.map(file => <link rel='preload' href={file}' />)

For example https://github.com/jamiebuilds/react-loadable#getbundles
P.S. I`m migrating to your loadable-components from react-loadable 👍

Flash of unstyled content (FOUC) issue

I get the the component without styles for a split second before the page fully loads. Using react-router v4, styled-components and create-react-app (unejected). The FOUC only happens on initial component mounts, subsequent mounts are either not affected or it happens too fast to see.

const Home = Loadable(() => import('./pages/Home'))

// ~~~ //

<Router>
  <Route exact path='/' component={Home}/>
</Router>

loadable-components issues with Asp.Net core SPA (JavascriptServices)

I am attempting to configure Server-side rendering for my Asp.Net core SPA application. the method getLoadableState does not wait for the async components to finish loading. I always get the loading component rendered:

const config = {
  LoadingComponent: () => <Loading />
};

export const Landing = loadable(() => import('../components/Landing'), {
  ...config
});

if i do this for each async component, my SSR works:

import * as Routes from './App/routes';
Routes.Graph.load();
const app = (
        <Provider store={store}>
          <StaticRouter
            basename={basename}
            context={routerContext}
            location={params.location.path}
          >
            <div>
              <Head />
              {/* <Body /> */}
              <Routes.Graph />
              <Foot />
            </div>
          </StaticRouter>
        </Provider>
      );
      params.domainTasks.then(() => {
        getLoadableState(app).then(loadableState => {
          resolve({
            html: renderToString(app).concat(loadableState.getScriptTag()),
            globals: { initialReduxState: store }
          });
        });
      }, reject); // Also propagate any errors back into the host application

i have tried the followind posts, but i am still getting issues with Async SSR:
https://marmelab.com/blog/2017/10/17/code-splitting.html
https://medium.com/smooth-code/introducing-loadable-components-%EF%B8%8F-646dd3ab0aa6 (sorry, your intro blogpost)

i have the full server code below, this is being called by an 'asp-prerender-module' taghelper on .Net Core. I am working off the ReactRedux SPA Template without Typescript.

/* eslint-disable no-console */
import 'babel-polyfill';

import * as React from 'react';
import { getLoadableState } from 'loadable-components/server';
import { renderToString } from 'react-dom/server';

import { Provider } from 'react-redux';

import { StaticRouter } from 'react-router-dom';
// import { replace } from 'react-router-redux';
// import { createMemoryHistory } from 'history';
import { createServerRenderer } from 'aspnet-prerendering';

import configureStore from './state/store/configureStore';

import Body from './App/Body';
import Head from './App/Header';
import Foot from './App/Footer';
// import * as Routes from './App/routes';

export default createServerRenderer(
  params =>
    new Promise((resolve, reject) => {
      // Prepare Redux store with in-memory history, and dispatch a navigation event
      // corresponding to the incoming URL

      const basename = params.baseUrl.substring(0, params.baseUrl.length - 1); // Remove trailing slash
      // const urlAfterBasename = params.url.substring(basename.length);
      // const store = configureStore(createMemoryHistory());

      const store = configureStore;
      // store.dispatch(replace(urlAfterBasename));

      // Prepare an instance of the application and perform an inital render that will
      // cause any async tasks (e.g., data access) to begin
      const routerContext = {};

      // Routes.Graph.load();
      const app = (
        <Provider store={store}>
          <StaticRouter
            basename={basename}
            context={routerContext}
            location={params.location.path}
          >
            <div>
              <Head />
              <Body />
              {/* <Routes.Graph /> */}
              <Foot />
            </div>
          </StaticRouter>
        </Provider>
      );
      renderToString(app);

      // If there's a redirection, just send this information back to the host application
      if (routerContext.url) {
        resolve({ redirectUrl: routerContext.url });
      }

      // Once any async tasks are done, we can perform the final render
      // We also send the redux store state, so the client can continue execution where the server left off
      params.domainTasks.then(() => {
        getLoadableState(app).then(loadableState => {
          resolve({
            html: renderToString(app).concat(loadableState.getScriptTag()),
            globals: { initialReduxState: store }
          });
        });
      }, reject); // Also propagate any errors back into the host application
    })
);

is it possible to name the bundle?

i never saw it works but i saw implementation of dynamic import using this syntax to give meaningful names to the bundles (unlike 1.lib.bundle. ):

for example:
import('./pages/About' /* webpackChunkName = 'about' */)

i've tried it using loadable-component with no success...
loadable(() => import('./pages/About' /* webpackChunkName = 'about' */))

please advise if it is supported or planned

best regards and thanks for this useful library

Webpack magic comments not supported?

First of all, thanks for your effort on this topic!

I've rapidly tried your solution on my project, and seems to work fine, here my observations (with a console.log on the loadable component render):

  • render seems to be fired 3 times: 1 on the client side, 2 on the server side.
  • the application broke if you try to import the component using webpack magic comments

The fact that you render only one time on the client side its awesome (would be perfect one time on the server side too), but I'd like to use magic comments, any plan on this?

Add a .babelrc example

Under Readme's Configuring Babel section says:

"To have a different configuration for client and server, you can use Babel env option."

But what I get from Babel env option docs is that that works for environments like development or production, and not for client or server. How can this can get achieved?

Thanks!

BTW: loadable-components is an awesome library with a good documentation!

[Error] Server and Client modules are not sync

Hi, I setup this library with SSR and currently I'm doing code-splitting for redux reducer based on this solution. But I encounter an error as following:

2018-02-11 2 47 56

BTW, if I remove the loadComponents().then( ... ) from my client.js the error gone but another error occurs as following:

2018-02-11 2 59 16

Any suggestion for it?

Question: Load all component from client begins

Hi I like this solution for SSR code splitting, but I have a question about load all component from client begins:

// Load all components needed before starting rendering
loadComponents().then(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById('main'),
  )
})

So the client also load unneeded code, didn't have code splitting benefit?

TypeError: getComponent is not a function

I am trying this library for the first time and am receiving an error. I replaced this existing import, which works:

import RefreshRequired from './RefreshRequired/RefreshRequired';

with this:

const LoadableComponent = loadable(import('./RefreshRequired/RefreshRequired'));

But I receive this error. I am using React 16.

Uncaught TypeError: getComponent is not a function
    at Function.load (loadable.js:61)
    at LoadableComponent.componentWillMount (loadable.js:79)
    at LoadableComponent.componentWillMount (createPrototypeProxy.js:44)
    at callComponentWillMount (react-dom.development.js:9777)
    at mountClassInstance (react-dom.development.js:9834)
    at updateClassComponent (react-dom.development.js:10216)
    at beginWork (react-dom.development.js:10605)
    at performUnitOfWork (react-dom.development.js:12573)
    at workLoop (react-dom.development.js:12682)
    at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
load @ loadable.js:61
componentWillMount @ loadable.js:79
componentWillMount @ createPrototypeProxy.js:44
callComponentWillMount @ react-dom.development.js:9777
mountClassInstance @ react-dom.development.js:9834
updateClassComponent @ react-dom.development.js:10216
beginWork @ react-dom.development.js:10605
performUnitOfWork @ react-dom.development.js:12573
workLoop @ react-dom.development.js:12682
callCallback @ react-dom.development.js:1299
a.bugsnag @ bugsnag-3.min.js:1
invokeGuardedCallbackDev @ react-dom.development.js:1338
invokeGuardedCallback @ react-dom.development.js:1195
performWork @ react-dom.development.js:12800
scheduleUpdateImpl @ react-dom.development.js:13185
scheduleUpdate @ react-dom.development.js:13124
enqueueSetState @ react-dom.development.js:9646
webpackJsonp../node_modules/react/cjs/react.development.js.ReactComponent.setState @ react.development.js:218
onStateChange @ connectAdvanced.js:205
onStateChange @ createPrototypeProxy.js:44
dispatch @ createStore.js:186
dispatch @ VM8639:2
(anonymous) @ breadcrumbLogger.js:16
(anonymous) @ webrtcVideo.js:106
(anonymous) @ index.js:14
(anonymous) @ errorCatcher.js:26
dispatch @ applyMiddleware.js:45
(anonymous) @ conversationsActions.js?4cf9:112
Promise resolved (async)
(anonymous) @ conversationsActions.js?4cf9:112
(anonymous) @ index.js:11
(anonymous) @ errorCatcher.js:26
dispatch @ VM8639:2
(anonymous) @ bindActionCreators.js:7
Promise resolved (async)
(anonymous) @ index.jsx:80
Promise resolved (async)
(anonymous) @ index.jsx:76
Promise resolved (async)
PlainChatApp @ index.jsx:73
instantiate @ createClassProxy.js:91
PlainChatApp @ VM9436:4
constructClassInstance @ react-dom.development.js:9760
updateClassComponent @ react-dom.development.js:10215
beginWork @ react-dom.development.js:10605
performUnitOfWork @ react-dom.development.js:12573
workLoop @ react-dom.development.js:12682
callCallback @ react-dom.development.js:1299
a.bugsnag @ bugsnag-3.min.js:1
invokeGuardedCallbackDev @ react-dom.development.js:1338
invokeGuardedCallback @ react-dom.development.js:1195
performWork @ react-dom.development.js:12800
scheduleUpdateImpl @ react-dom.development.js:13185
scheduleUpdate @ react-dom.development.js:13124
scheduleTopLevelUpdate @ react-dom.development.js:13395
updateContainer @ react-dom.development.js:13425
(anonymous) @ react-dom.development.js:17105
unbatchedUpdates @ react-dom.development.js:13256
renderSubtreeIntoContainer @ react-dom.development.js:17104
render @ react-dom.development.js:17129
renderApp @ index.jsx:23
./public/index.jsx @ index.jsx:35
__webpack_require__ @ bootstrap 43a581b6ca41ecf5e187:693
fn @ bootstrap 43a581b6ca41ecf5e187:114
2 @ chat-bundle.js:31613
__webpack_require__ @ bootstrap 43a581b6ca41ecf5e187:693
webpackJsonpCallback @ bootstrap 43a581b6ca41ecf5e187:25
(anonymous) @ chat-bundle.js:1
bugsnag-3.min.js:1 The above error occurred in the <LoadableComponent> component:
    in LoadableComponent (created by AppHeader)
    in div (created by AppHeader)
    in AppHeader (created by PlainChatApp)
    in div (created by PlainChatApp)
    in PlainChatApp (created by DragDropContext(PlainChatApp))
    in DragDropContext(PlainChatApp) (created by Connect(DragDropContext(PlainChatApp)))
    in Connect(DragDropContext(PlainChatApp))
    in Provider
    in AppContainer
    in ErrorBoundary

loadable components is pre-loading all the route chunks from start

Hi,

I'm not sure if this is expected behavior in development, a bug or a missconfiguration. I have the following loadable routes:

import loadable from 'loadable-components'

// Component split-code (lazy load)
export const Dashboard = loadable(() => import('./Dashboard'))
export const SignIn = loadable(() => import('./SignIn'))

export const PasswordReset = loadable(() => import('./PasswordReset'))
export const PasswordResetEdit = loadable(() => import('./PasswordResetEdit'))

export const Users = loadable(() => import('./Users'))
export const User = loadable(() => import('./User'))
export const EditUser = loadable(() => import('./EditUser'))
export const NewUser = loadable(() => import('./NewUser'))

And they are imported and used with react router v4 the following way:

// We only need to import the modules necessary for initial render
import React from 'react'
import PropTypes from 'prop-types'
import { Switch, Route } from 'react-router-dom'
import PrivateRoute from 'components/PrivateRoute'
import * as LoadableRoutes from './routes'

export const Routes = (props) => {
  return (
    <Switch>
      <Route exact path='/sign-in' render={routeProps => <SignIn {...routeProps} persistor={props.persistor} />} />
      <Route exact path='/passwords/new' component={LoadableRoutes.PasswordReset} />
      <Route exact path='/passwords/edit' component={LoadableRoutes.PasswordResetEdit} />
      <PrivateRoute exact path='/' component={LoadableRoutes.Dashboard} persistor={props.persistor} />
      <PrivateRoute exact path='/users' component={LoadableRoutes.Users} persistor={props.persistor} />
      <PrivateRoute exact path='/users/new' component={LoadableRoutes.NewUser} persistor={props.persistor} />
      <PrivateRoute exact path='/users/:id' component={LoadableRoutes.User} persistor={props.persistor} />
      <PrivateRoute exact path='/users/:id/edit' component={LoadableRoutes.EditUser} persistor={props.persistor} />
    </Switch>
  )
}

Routes.propTypes = {
  persistor: PropTypes.object.isRequired
}

export default Routes

This <Switch> component is imported from the main AppContainer like:

import { PersistGate } from 'redux-persist/integration/react'
import { Router } from 'react-router-dom'
import Routes from 'routes'

class AppContainer extends Component {

... [code ommited for brevity]...

<Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
              <div style={{ height: '100%' }}>
                <ReduxToastr
                  timeOut={toastrDefaultTimeout}
                  newestOnTop={newToastrAlwaysOnTop}
                  preventDuplicates
                  position='top-right'
                  transitionIn='fadeIn'
                  transitionOut='fadeOut'
                  progressBar />
                <Router history={history}>
                  <CoreLayout {...this.props} >
                    <Routes persistor={persistor} />
                  </CoreLayout>
                </Router>
              </div>
            </PersistGate>
          </Provider>
}

export default AppContainer

And here you can see how all the chunks are loaded right from the start (cache disabled):

image

Is this the expected behaviour and it will work different in production ? I'm using webpack v4.

Thanks!

dynamic import not work ?

Description

Hi, I use webpack4 and loadable-components for code splitting and dynamic import , I see the webpack code splitting success , but dynamic import not work , here is my code

// router.js
import loadable from 'loadable-components'
export const Home = loadable(() => import('./Home'))
export const About = loadable(() => import('./About'))
export const Topics = loadable(() => import('./Topics'))

// RouterLink.js
<BrowserRouter>
    <Route exact path="/" component={Routes.Home} />
    <Route path="/about" component={Routes.About} />
    <Route path="/topics" component={Routes.Topics} />
</BrowserRouter>

// App.js
class App extends Component {
    render(){
      <RouterLink />
    }
}

DId the loadable-components need config babel ? I try to config loadable-components/babel in .babelrc , but it not work , here is my .barbelrc

{
  "presets": [
    [
      "env",
      {
        "loose" : true,
        "modules": false
      }
    ],
    "stage-0",
    "react"
  ],
  "plugins": [
    "loadable-components/babel",
    "react-hot-loader/babel"
  ]
}

Environment

node: 8.9.0
react-hot-loader: 4.1.1
webpack :4.6.0
webpack-dev-server: 3.1.3
loadable-components: 1.4.0

Error when working with snapshots

I'm using a tool similar to react-snap, and can verify that I'm setting window.__LOADABLE_COMPONENT_IDS__ = [2].

Then, as specified in the react-snap docs, I'm doing the following:

loadComponents().then(() => hydrate(...))

However, loadComponent throws the following error:

Uncaught TypeError: Cannot read property 'Symbol(loadable)' of undefined
    at loadComponents.js:21
    at Array.map (<anonymous>)
    at r (loadComponents.js:20)
    at Object.<anonymous> (index.js:27)
    at t (bootstrap 056c276262f9a7299fc4:49)
    at Object.<anonymous> (main.661463cf.js:3846)
    at t (bootstrap 056c276262f9a7299fc4:49)
    at bootstrap 056c276262f9a7299fc4:144
    at bootstrap 056c276262f9a7299fc4:144

The problem is here, since componentTracker.get(id) is undefined.

I set breakpoints in componentTracker, and found that the components object in componentTracker.js is just an empty object. This is expected - loadComponent is the first thing to get called.

This is what my index.js looks like:

import React from 'react';
import { hydrate, render } from 'react-dom';
import App from './App';

import { loadComponents } from 'loadable-components';
import { getState } from 'loadable-components/snap';

const snapSaveState = () => {
  document.querySelector('html').setAttribute(
    'data-snap-state',
    JSON.stringify(getState())
  );
};

const rootElement = document.getElementById('root');
if (rootElement.hasChildNodes()) {
  const snapState = document.querySelector('html').getAttribute('data-snap-state');

  if(snapState) {
    console.log(JSON.parse(snapState));  // This shows the expected value
    Object.entries(JSON.parse(snapState)).forEach(([key, value]) => window[key] = value);
  }

  loadComponents().then(() => {
    console.log('hydrating'); // This is never called
    hydrate(<App />, rootElement);
  });
} else {
  render(<App />, rootElement, snapSaveState);
}

I'm using a data-attribute on the HTML tag instead of setting an inline script tag because of CSP limitations. However, in essence, this shouldn't change anything, since I've made the correct global available before calling loadComponents.

Am I doing something wrong?

Strange behaviour

I assume I miss something. Source of the application stereobooster/an-almost-static-stack#2.

Generated html looks like

<script type="text/javascript">window.__LOADABLE_COMPONENT_IDS__ = [0];</script>
<script type="text/javascript" src="/static/js/main.9d08ca0c.js"></script>
<script type="text/javascript" src="/static/js/0.cef59dc3.chunk.js" charset="utf-8"></script>
<script type="text/javascript">window.bootReactSnapApp && window.bootReactSnapApp();</script>

and window.bootReactSnapApp stands for

window.bootReactSnapApp = () => {
    loadComponents().then(() => {
      hydrate(AppWithRouter, rootElement);
    });
};

The strange thing is the network waterfall looks like this:

screen shot 2017-11-23 at 20 08 18

Why there is chunk 2? This page does not use this chunk. Any ideas what is wrong here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.