gregberge / loadable-components Goto Github PK
View Code? Open in Web Editor NEWThe recommended Code Splitting library for React ✂️✨
Home Page: https://loadable-components.com
License: MIT License
The recommended Code Splitting library for React ✂️✨
Home Page: https://loadable-components.com
License: MIT License
Thank you for this great library. I have integrated it with my React starter with painless. I like your design philosophy. If you can integrate Delay and Timeout into this library I think it would be great. These features are basic necessary for async component 👍
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
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-plugin
could 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
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?
calling ReactDOM.hydrate causes an error:
Did not expect server HTML to contain a <div>
in <div>
.
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:
BTW, if I remove the loadComponents().then( ... )
from my client.js
the error gone but another error occurs as following:
Any suggestion for it?
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!
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?
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} />);
},
});
I find ``loadable-components``` very good, but no provide TypeScript support
You can provide TypeScript support
So much the better 😃
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?
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
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
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...
First off, great work! I love the simplicity of this project.
getLoadableState()
should return a promise.
getLoadableState()
returns an instance of DeferredState
if no queries are found.
yarn add loadable-components
.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.
As suggested in #14 it would be awesome to have a TypeScript support for Loadable Components.
This issue consist by adding Loadable Components types on DefinitelyTyped.
You can inspire by react-loadable types.
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!
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):
Is this the expected behaviour and it will work different in production ? I'm using webpack v4.
Thanks!
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..
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>
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)} />
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:
Why there is chunk 2? This page does not use this chunk. Any ideas what is wrong here?
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
})
);
Hi I upgraded to v1.2.0 but I found my async component doesn't be hot reloaded without this way.
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 })
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] } } ]
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?
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
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?
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.
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');
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!
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
(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)
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 👍
import { getLoadableState } from 'loadable-components';
console.log(getLoadableState) // undefined
"loadable-components": "0.2.0",
Hi, how I can fix this?
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.
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):
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?
@neoziro I'm not sure if this issue belongs to react-router or loadable-components. After I integrating loadable-components with my project. I found the staticContext
won't work.
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! 👍
I use this library with react-hot-loader beta.21. But it seems not support hot reloading yet? Will this library support it?
Getting react error when imported modules have same name but located in different folders e.g. index.js
or page.js
reproduced in repo
please follow instruction
As title. I want to know does this library support webpack v4 yet?
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
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?
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
@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
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 called
loadableState.getScriptTag() server-side
. I dive into the module and I put a console.log(window)
in the loadComponents()
function, the result contains this:
but if I do console.log(window[LOADABLE_STATE]);
is returning undefined
.
Do you have any clue about what is happening?
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
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"
]
}
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.