ctrlplusb / react-async-component Goto Github PK
View Code? Open in Web Editor NEWResolve components asynchronously, with support for code splitting and advanced server side rendering use cases.
License: MIT License
Resolve components asynchronously, with support for code splitting and advanced server side rendering use cases.
License: MIT License
Hi,
I'm build a route with with asyncComponent
and got an unrelated error, but then component wrapped with asyncComponent
, there is no any errors in browser console, just clean screen. I can catch it only via React dev tool:
but, if I use component without wrapping in asyncComponent
, then I got an error in browser console, like this:
Simplification looks really nice. What would be great is to add old disable
flag from code-split.
This will help to switch between hot reload or async-component in runtime/build.
Something like
// client-entry.js
import { withAsyncComponents } from 'react-async-component'; // π
const app = <MyApp />;
// adding disabled based on hot reload env var π
const render = (app) => withAsyncComponents(app, process.env.HOT_RELOAD)
.then((result) => {
const {
appWithAsyncComponents: Root
} = result;
ReactDOM.render(
<ReactHotLoader>
<Provider store={store}>
<Router><Root/></Router>
</Provider>
</ReactHotLoader>,
document.getElementById('app'));
});
// Hot reloading on the client
if (process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./app/routing/Root', () => {
const Root = require('./app/routing/Root').default; // eslint-disable-line no-shadow
render(Root);
});
}
const Root = require('./app/routing/Root').default; // eslint-disable-line no-shadow
render(Root);
Similar on server side if needed
withAsyncComponents(app, process.env.HOT_RELOAD)
// π and you get back a result object.
.then((result) => {
const {
// βοΈ The result includes a decorated version of your app
// that will have the async components initialised for
// the renderToString call.
appWithAsyncComponents,
// This state object represents the async components that
// were rendered by the server. We will need to send
// this back to the client, attaching it to the window
// object so that the client can rehydrate the application
// to the expected state and avoid React checksum issues.
state,
// This is the identifier you should use when attaching
// the state to the "window" object.
STATE_IDENTIFIER
} = result;
const appString = renderToString(appWithAsyncComponents);
What do you think?
I can see great value if having ability to run hot reload for rapid dev of something during development (even if it not really working that great) and being able to switch to code split when needed - like production or debugging of something
I am using react-router v4
and I have this code
import React from 'react'
import Route from 'react-router-dom/Route'
import LazilyLoad from './LazilyLoad'
const Async = route => (
<Route
path={route.path}
render={props => (
<LazilyLoad render={route.component} >
{Component => (
<Component {...props} routes={route.routes} />
)}
</LazilyLoad>
)}
/>
)
Where LazilyLoad
is my own async component. I would like to switch to react-async-component
since your code is way better, but I cannot manage to send props to the resolved component.
Right now I have this code (where route.component
return System.import(...)
)
const Async = route => (
<Route
path={route.path}
component={
asyncComponent({
resolve: () => route.component,
})
}
/>
)
But I get
Warning: Failed prop type: The prop
routes
is marked as required inDashBoard
, but its value isundefined
.
since I not longer have routes={route.routes}
So, is it possible to send props to a resolved component ? If yes, how ? If no, is it desired feature that maybe I can try to implement and PR ?
I'm trying to optimize my loading strategy. My number one concern is SEO. I want server side rendering, but I also want the end user to have a smart incremental loading experience.
I was wondering if it would be possible to have a mode that renders on the server, but defers client side loading. This way, you could have something on screen, and available to the Google bot, that wasn't interactive yet. A LoadingComponent in this scenario could be designed as a transparent overlay, to be hidden once client side loading completes.
Just wondering what do you recommend if I wanted to log exceptions using Sentry. The async components are catching the exception so I'm wondering is there a way to pass a handler to know when an error occurred. Or maybe are we able to get an option to not catch the exceptions?
Hey man,
So I am having an issue that is killing me. I am not sure if its related to async component or what - i know there are generally issues with react-router and HMR and adding the async is just the cherry on top :-P
Anyway I see you've dealt with literally every issue I am running into so I gotta ask!
On my first update of an async route it does update but I get a warning:
main.js:107 [HMR] unexpected require(./ui/screens/Home/Home.js) from disposed module ./ui/routes.js
then on the next update it fails saying the given path is not accepted.
Any ideas in general? I can provide more information of course but I am pretty sure I'm just doing whatever you were when you had the issues.
I need to do an optimisation run over the tree walk algorithm. Some clever tracking will allow us to do early bails and avoid unnecessary walks.
I was wondering how to combine this lazy loading with "fetchData()" static methods used by Apollo for prefetching data server-side during SSR. This is implemented in getDataFromTree
(see: http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree)
I see you have a custom solution for loading data which might not work for us, as we need Apollo support. Currently it seems like Apollo's logic is not waiting for the chunks which actually needs the relevant data.
Maybe you can share a hint in the docs.
I'd like to see support for loading locale-specific i18n message files together with the actual view component. This would allow for route-splitted translation files which, as the view itself, are just loaded on demand e.g. for usage with IntlProvider
by react-intl.
Conceptually it might look like:
const AboutView = createLazyComponent({
load: (language) => {
return [
import("./views/About"),
import("./views/About." + language + ".json")
]
}
})
but int theory instead of an array we could also use an object/dict for being more specific:
const AboutView = createLazyComponent({
load: (language) => {
return {
view: import("./views/About"),
messages: import("./views/About." + language + ".json")
}
}
})
If there are messages it would probably be a good idea to use IntlProvider
with the messages given for wrapping the dynamically loaded view. It might be also cool to use injectIntl
for offering the intl
API via props to the loaded view.
What do you think?
Hi :)
Sorry for the not so useful issue here but thanks a bunch for the lib!
I was starting to have a similar API myself but couldn't manage my way out.
If I can be of any help here, feel free to reach out. I'll be glad to give a hand!
Thanks for building this package, it was simple to implement.
I'm looking for suggestions on the best way to resolve an async component from another async component. I basically want to preload another component path after another resolves. This is just an optimization to remove the loading on a path users are likely to take next. Since they are just components, I guess I could pre initialize the next component in componentDidMount of the resolved component. Any other ideas?
It might be nice to have a built in mechanism for this like:
asyncComponent({
...
thenResolve: [AnotherAsyncComp, AnotherAsyncComp2]
})
Hello
My application is server side rendered.
Here is my asyncComponent which load a HomeBackground depending on a marketing code :
export default asyncComponent({
resolve: () => new Promise(resolve => {
require.ensure([], () => {
const path = getStore().getState().marketing.code || "default";
const component = require("./" + path + "/HomeBackground.jsx");
resolve(component);
}, "home-background-loader");
}),
serverMode: "resolve"
});
In dev mode, everything works perfectly.
However when I build the app for production, I encounter the following issue (marketing code here is "ul") :
I wonder if dynamic require is handled by react-async-component ?
What am I doing wrong ?
Best regards
babel-node is not meant for production.
http://babeljs.io/docs/usage/cli/#babel-node
I tried out the async resolve on a React Universally project and I ran into a lot of nasty little issues that were hard to debug. I'm still not sure what exactly was going wrong. And I would like to find out how to avoid this
The idea was basically to reduce the size of the initial loaded component and take advantage of code splitting by using
export default asyncComponent({
// include home and about route in same chunk e.g main
resolve: () =>
new Promise(resolve =>
require.ensure(
[],
(require) => {
resolve(require('./AdminRoute'));
},
'admin',
),
),
LoadingComponent: () => <Loading />,
});
instead of
export default asyncComponent({
resolve: () => System.import('./AdminRoute'),
LoadingComponent: () => <Loading />,
});
However, I ran into issues like loading a location and then navigating to a another location and getting errors like this:
which translates to:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
This only happened on production.
I initially thought this was due to caching since we are using CloudFlare on the front end, but I added some rules there to prevent caching of /index.html, and I still had the issues.
So, in the end I resorted to going back to big fat deployment file since the pressure was on to deliver. But I would really like to understand how to deal with chunking properly so these errors don't occur and if they do are easier to debug.
I have a container that loads a component async with react-async-component and then perform the connection with redux's state.
It works nicely, however, I could not find a way to test it. The component (obviously) is not ready when the tests runs. In this case enzyme finds an empty react component.
Code examples:
ForgotPage.js
// @flow
import { createAsyncComponent } from 'react-async-component';
import { connect } from 'react-redux';
import requestPasswordRecovery from '../../shared/state/actions/passwordRecovery';
const Forgot = createAsyncComponent({
resolve: () => new Promise(resolve =>
// $FlowFixMe
require.ensure([], (require) => {
resolve(require('../components/Forgot'));
}, 'forgot'),
),
ssrMode: 'boundary',
});
const mapStateToProps = state => ({
passwordRecovery: state.passwordRecovery,
});
const mapDispatchToProps = dispatch => ({
requestPasswordRecovery: data => dispatch(requestPasswordRecovery(data)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Forgot);
This code does not work with my integration tests. If, instead of loading it async I just import it, tests run fine.
Is there a way to get this working with tests?
Trying to convert react-universally to use this now while I build up the routes management structure and having issues.. .my app is mounting twice, it runs completely and renders then withAsyncComponents forces it to launch again and it causes all kinds of havoc.
const RenderedApp = ({ App, store }) => (
<ReduxProvider store={store}>
<BrowserRouter>
<App store={store} />
</BrowserRouter>
</ReduxProvider>
)
function renderApp(store) {
/* Get our Rendered App & Redux Store */
// π run helper on your app and get back a result object.
// βοΈ The result includes a decorated version of your app
// that will allow your application to use async components
// in an efficient manner.
withAsyncComponents(<RenderedApp App={App} store={store} />).then(
({ appWithAsyncComponents }) => {
console.log('With Async', appWithAsyncComponents)
render(appWithAsyncComponents, container)
}
)
}
As you can see it calls the With Async log which renders our app however it has already finished rendering the app in the first place when this is called.
It is unfortunately too flaky to use this library with RHL.
I recommend using webpack's standard hot module reloading instead.
I am going nuts with my AsyncComponent usage. It's awesome some of the use cases :-)
Given this I have found that the module.hot API breaks things. I need to add some behaviour that gives us specific handling for module.hot... I believe.
Hi, there.
I have next usage question.
Before import module I need to fetch API service to resolve what module I have to import. My application uses SEO-friendly routing. For example:
/my-very-nice-article
corresponds ArticlePage
component/statistics-for-my-other-very-nice-article
corresponds ArticleStatisticsPage
So in resolve function of my async component I need to get current location from props
(I use react-router v4
).
All my routing consists of the next line:
<Route path="*" component={AsyncComponentResolver}/>
I didn't find any information how to use props
in resolve
function and write wrapper component:
class AsyncComponentResolver extends Component {
render(){
const {pathname} = this.props.location;
const AsyncComponent = asyncComponent({
resolve: () => {
return API.fetchRouteInformation(pathname)
.then(({modulePath}) => import(modulePath))
},
LoadingComponent: ({ match }) => <div>Resolving {match.url}</div>
});
return <AsyncComponent {...this.props}/>
}
}
But this solution doesn't work in stage of server-side rendering and I have <div>Resolving /my-very-nice-article</div>
in rendered HTML instead of rendered ArticlePage
component.
Thanks! It will be very nice if you show me the right way.
I think you will need to do some extra error handling here. This is a bug in my code I believe but react reports it as an error of AsyncComponent which will make debugging difficult:
invariant.js:44 Uncaught (in promise) Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `AsyncComponent`.
I've found that when I use this to async load a component, if I navigate to a different route that uses the same component, the app briefly hits the loading component, before re-mounting the resolved component. No network request is made, since it's already been loaded, but I do get a brief flash of the loading component. Is there any way to prevent this, or is this known/expected behavior?
Hey @ctrlplusb, this library is great!
But I know when react-loadable launched, you'd considered merging the projects. Wondering if that it still on the cards? And if not, how do you compare the two libraries (pros/cons)?
Any tips in helping make that decision are much appreciated π
Hi! Thank you for a nice library!
I'm trying to saddle server-side rendering for my app so I'm wondering...
Is it possible to use react-async-component to load all the data which is fetching in componentWillMount method at the server and then return the fully loaded component to the client? So instead of preloaders the user will be able to see a page with some loaded data.
I'm trying to implement such example, but I'm basically having 2 times of calling render and componentWillMount method without waiting for promises.
Hello,
First, thank you for your great work ! I daily use few of your packages (async-boostrapper, react-universally etc)
I have an issue with react-async-component with SSR, which does not rehydrate components local states. Is this a feature this package is supposed to offer, or did I misunderstood allowing client side state rehydration, avoiding React checksum errors.
?
I have no simple example to submit (SSR+CodeSplit is not simple :p) but this can be reproduced with react-universally in a few steps :
shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js
componentWillMount() {
this.setState({counter: Math.random()});
}
--> State is not rehydrated, react checksum errors appears etc.
Thank you !
Getting the always loved Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the AsyncComponent component.
I noticed when perusing the code there was no handling of componentWillUnmount to cancel your promises so that they don't request this.setState().
Hi @ctrlplusb ! Nice library!
I am implementing it and it all works perfectly. Although I think I am not setting it up properly.
const renderApp = app => {
let shell = <AppContainer>
<WithStylesContext onInsertCss={styles => styles._insertCss()}>
<MuiThemeProvider muiTheme={theme}>
<Provider store={store}>
<BrowserRouter router={app}/>
</Provider>
</MuiThemeProvider>
</WithStylesContext>
</AppContainer>;
withAsyncComponents(shell).then(({appWithAsyncComponents}) =>
render(appWithAsyncComponents, rootEl),
);
};
renderApp(SpotifyApp);
if (NODE_ENV === 'development' && module.hot) {
module.hot.accept("./app.tsx", () => {
const NextApp = require("./app.tsx").default;
renderApp(NextApp);
});
}
// also wondering how to name the splitted chunks?
const AsyncDashboard = createAsyncComponent({
resolve: () => new Promise(resolve =>
require.ensure([], (require) => {
resolve(require("./components/dashboard/dashboard"));
}, "dashboard.js")),
});
export let SpotifyApp = () => {
return (
<div>
<Match exactly pattern="/" component={Shell(AsyncProduct)}/>
<Match exactly pattern="/dashboard" component={Shell(AsyncDashboard)}/>
<Miss component={NoMatch}/>
</div>
)
};
export default () => (request, response) => {
const context = createServerRenderContext();
const result = context.getResult();
if (result.redirect) {
response.redirect(302, `${result.redirect.pathname}${result.redirect.search}`);
} else {
if (result.missed) {
response.status(404);
} else {
response.status(200);
}
let css = []; // CSS for all rendered React components
let App =
<WithStylesContext onInsertCss={styles => css.push(styles._getCss())}>
<MuiThemeProvider muiTheme={getMuiTheme({userAgent: request.headers['user-agent']})}>
<Provider store={createStore(allReducers,allReducersInitial)}>
<ServerRouter location={request.url} context={context}>
<SpotifyApp/>
</ServerRouter>
</Provider>
</MuiThemeProvider>
</WithStylesContext>;
withAsyncComponents(App)
.then((result) => {
const {
appWithAsyncComponents,
state,
STATE_IDENTIFIER
} = result;
const markup = ReactDOMServer.renderToString(appWithAsyncComponents);
let SerialState = require('serialize-javascript')(state);
console.log(SerialState);
response.send("<!DOCTYPE html>" +
ReactDOMServer.renderToStaticMarkup(
<UniversalShell css={css}
state={SerialState}
STATE_IDENTIFIER={STATE_IDENTIFIER}
userAgent={request.headers['user-agent']}
content={markup}/>
));
});
}
};
I am using renderToStaticMarkup so I was not able to set the window object.
<script type="text/javascript"
dangerouslySetInnerHTML={
{__html:
"window.__REACT_ASYNC_COMPONENTS_STATE__ = "+ this.props.state+";"+
"var HEY = "+ this.props.state+";"
}
}
/>
On the console I can get HEY but cannot get window.REACT_ASYNC_COMPONENTS_STATE
Weird thing is that the state is always {"resolved":{}}
i was expecting this information to be really important, but it seems it is always empty even though it works flawlessly.
Hello. Can I get current fetching progress of component?
Example: XMLHttpRequest has event 'progress'. Size of my async component is 500kb. I want see progress bar on top of the page with progress value in LoadingComponent
Works on client, but cannot find module on server side:
.babelrc
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions"]
}
}],
"es2015",
"react",
"stage-3"
],
"plugins": [
"react-hot-loader/babel"
]
}
webpack.config
[{
name: 'client',
target: 'web',
entry: {
bundle: [
'react-hot-loader/patch',
'webpack-hot-middleware/client',
'webpack/hot/only-dev-server',
path.join(srcDir, 'index.js')
],
vendor: vendor
},
output: {
path: webpackProdConfig.output.path,
chunkFilename: '[name].js',
filename: '[name].js',
publicPath: '/'
}
...
},
{
name: 'server',
target: 'node',
entry: path.resolve(__dirname, '../server/serverRenderer.js'),
output: {
path: webpackProdConfig.output.path,
filename: '[name].js',
chunkFilename: '[name].js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader', 'eslint-loader'],
exclude: /node_modules/
}
]
}
...
}]
AsyncAbout/index.js
import { asyncComponent } from 'react-async-component';
export default asyncComponent({
resolve: () => new Promise(resolve =>
require.ensure([], (require) => {
resolve(require('./About'));
}, 'about')
)
});
AsyncAbout/About.js
import React from 'react';
export default () => <h1>About</h1>;
Compilation output:
Child client:
chunk {0} post-page.js, post-page.js.map (post-page) 5.57 kB {2} [rendered]
chunk {1} about.js, about.js.map (about) 610 bytes {2} [rendered]
chunk {2} bundle.js, bundle.js.map (bundle) 305 kB {3} [initial] [rendered]
chunk {3} vendor.js, vendor.js.map (vendor) 1.22 MB [entry] [rendered]
Child server:
chunk {0} post-page.js (post-page) 5.57 kB {2} [rendered]
chunk {1} about.js (about) 610 bytes {2} [rendered]
chunk {2} main.js (main) 1.21 MB [entry] [rendered]
webpack: Compiled successfully.
Files are served with dev-server so they are not written to disk.
After navigating to http://localhost:3000/about, got this error on the server side:
Failed to resolve asyncComponent
{ Error: Cannot find module './about.js'
at Function.Module._resolveFilename (module.js:469:15)
at Function.Module._load (module.js:417:25)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Function.requireEnsure [as e] (/var/www/public/main.js:41:25)
at /var/www/public/main.js:18928:34
at resolve (/var/www/public/main.js:18927:12)
at getResolver (/var/www/public/main.js:25629:24)
at AsyncComponent.resolveModule (/var/www/public/main.js:25719:16)
at doResolve (/var/www/public/main.js:25671:25) code: 'MODULE_NOT_FOUND' }
I'm trying to do ssr with injected reducers using redux. However I'm seeing no store on the context when the renderToString
function is executed.
I'm essentially following this repo https://github.com/dlebedynskyi/react-playground and specifically i don't see this.context.store
in this component: https://github.com/dlebedynskyi/react-playground/blob/master/src/app/store/withAsyncReducers.js
That repo uses an outdated version of react async component and react async bootstrapped so I'm basically trying to do the same with the updated libraries. In the older version a variable appWithAsyncComponents
would exist after the bootstrap but now that doesn't exist. Would you have any idea why the context is an empty object when rendering to string but exists before that? Any guidance would be appreciated.
//... In my middleware fn...
const app = (
<AsyncComponentProvider asyncContext={asyncContext}>
<Provider store={store}>
<StaticRouter location={request.url} context={routerContext}>
<MyApp />
</StaticRouter>
</Provider>
</AsyncComponentProvider>
)
// this.context.store exists hereπ
asyncBootstrapper(app).then(() => {
// app doesn't have store here π
const appString = renderToString(app)
This type of behaviour is only required on the server.
Warning: Failed child context type: Cannot read property 'asyncComponentsAncestor' of undefined
in AsyncComponent (created by Route)
in Route
in div
in Router (created by ConnectedRouter)
in ConnectedRouter
in Provider
looks like there is no validation - that context could not exist - so it throws that error - but after that works as expected (from my point of view)
AsyncLogin.js
import { asyncComponent } from 'react-async-component';
export default asyncComponent({
resolve: () => import('./index')
});
Use this plug-in for asynchronous loading components, other browsers can run, but in IE10 + all the browsers are reported on this error, do not know why, when the package using the babel compiler. how to solutionοΌ thanks
I'm using react-async-component
in a SSR context where all async components are defer=true
. In this case there is no need to transfer state using STATE_IDENTIFIER
from the server.
Wrapping the app in withAsyncComponents
makes it harder to get working with data fetchers. We use redial
and I'm quite happy with it.
I've been able to implement react-async-component
synchronously by using AsyncComponentProvider
directly.
import AsyncComponentProvider from 'react-async-component/commonjs/AsyncComponentProvider';
...
const app = (
<AsyncComponentProvider execContext={createExecContext()}>
<MyApp />
</AsyncComponentProvider>
);
This uses internal API and I had to copy createExecContext
into my project, but it works. I would like to have an official and simpler way of doing this.
Just tried an update to React 16 and it would appear an error with async component makes the app fail to load :(
warning.js:36 Warning: Failed child context type: Cannot read property 'asyncComponentsAncestor' of undefined
Says it is only a warning but my app no longer loads at all and this is the only error I can see..
Thanks for putting this together.
if (rehydrateState) {
if (!rehydrateState.resolved[id]) {
return false;
}
....
Not sure what rehydrateState.resolved[id] is referring to, but in the case of rehydrateState.resolved not defined it throws an error which I guess you should guard against its null-ability.
state: { resolved: execContext.getResolved() },
I think you kill state rehydration too soon before it gets to the client.
Currently to get around this I only requery store.getState() which is not ideal.
I'm having a little problem.
Say I have an async component that is encapsulated with react-async-component, like this:
const AsyncComponent = asyncComponent({
resolve: () => new Promise(resolve =>
require.ensure([], require => {
resolve(require('draft-js'))
}
)
})
then call its internal functions by refs
<AsyncComponent ref={c => this.editor = c} />
this.editor.focus();
but will get Uncaught TypeError: _this.editor.focus is not a function
error, and I use console.log(this.editor)
see it's a async component and has not focus function so that I can't access it.
I spent a lot of time but still have no idea how to fix it,
does anyone can help ? thanks π
I moved my app from code-split-component to react-async-component but the Async components don't seem to hot reload properly. When I make a change to an Async component I get the usual messages in Chrome console with no differences ([HMR].. ) however the displayed content doesn't change. If I change an Async component then also change a non-async component, the changes to both appear at the same time.
Relevant section of App.jsx:
import { withAsyncComponents } from 'react-async-component'
import store from './store'
import Root from './Root'
function renderApp(Param) {
const app = (
<AppContainer>
<Provider store={store}>
<Param />
</Provider>
</AppContainer>
)
withAsyncComponents(app).then(({ appWithAsyncComponents }) =>
ReactDOM.render(appWithAsyncComponents, rootElement), // eslint-disable-line
)
}
renderApp(Root)
if (module.hot) {
module.hot.accept(
'./Root',
() => renderApp(require('./Root').default), //eslint-disable-line
)
}
AppContent.jsx:
import IntroductionAsync from './IntroductionAsync'
<Match pattern="/" exactly component={IntroductionAsync} />
IntroductionAsync.jsx:
import { createAsyncComponent } from 'react-async-component'
const AsyncIntroduction = createAsyncComponent({
resolve: () => System.import('./Introduction'),
})
export default AsyncIntroduction
The problem occurs when I try to edit Introduction.jsx.
[email protected]
[email protected]
[email protected]
Is there something I'm doing wrong or is this a bug of some sort?
I see that you already have an index.d.ts file in the project. Would be great if that was included in the npm package, or atleast made available under @types/react-async-component
Hey, cool to see you have abstracted away the lazy loading handling for large React tree.
I found the approach much better than what react-router is suggesting to do!
I'm wondering, do you have any plan preloading dependencies on the client side during the idle time?
I have been implemented a naive solution, I'm pretty sure we could improve that. It's preloading everything.
If we were collecting some data. For instance, the order of lazing loading, we could even build a prediction model. I have heard that Facebook is using a machine learning algorithm to smartly load dependencies ahead of time.
Thanks for the awesome library! I can't tell if I'm doing something wrong or not, but I can't seem to resolve nested components. Walking the element tree after passing through withAsyncComponents
server-side describes nested async components as promises as yet unresolved:
{ path: '/test/foo',
component:
{ [Function: AsyncComponent]
childContextTypes: { asyncComponentsAncestor: [Object] },
contextTypes: { asyncComponents: [Function: bound checkType] },
displayName: 'AsyncComponent' },
},
}
The tree could be described as thus:
// in routes.js
const baseTestRoute = (
<Route
path={'/test'}
component={createAsyncComponent({
resolve: () => import('./routes/NestedTestRoute'),
})
}
/>
);
// in ./routes/NestedTestRoute.js
export default function NestedTestRoute({ match }) {
return (
<Switch>
<Route path={match.url} exact component={() => <h1>renders just fine, hooray!</h1>} />
<Route
path={`${match.url}/foo`}
component={createAsyncComponent({
resolve: () => import('./DeeplyNestedRoute'),
Loading: () => <h1>this displays on the server</h1>,
})}
/>
</Switch>
);
}
// in DeeplyNestedRoute.js
export default function DeeplyNestedRoute({ match }) {
return (
<Route path={`${match.url}/bar`} component={SomeComponentThatDoesNotRender} />
);
}
If you need me to stand up a small application demonstrating the problem, I can definitely do as such.
I tried to reimplement your guide: https://github.com/mqklin/react-async-component-bug
And I got this error:
What's wrong?
Hey, thanks for your work on this.
I use componentWillMount
to kick off some redux-thunk
s that do data fetching for my component on the server side.
Since react-async-component
walks the tree to find the async components, it triggers all of the componentWillMount
calls...then I still have to render the resulting appWithAsyncComponents
with react-dom
that invokes all of those componentWillMount
s again.
The result is that I end up hitting my API twice for everything...once while finding the async components, and once when actually rendering to string.
UPDATE: I also find when using react-resolver
that the same occurs. Essentially, any data fetching / expensive activity that's triggered by a pass over the tree would have to happen twice with this sort of approach.
Might be interesting to allow components to provide a prop to override the default behavior. For example, if some components conduct actions within their componentWillMount
function that we don't want the server to do and/or don't want to occur twice, we can override it by providing something like asyncComponentWillMount
or something similar. If not provided, default behavior applies.
First, this library is awesome!
Second, I want to show off my TypeScript and ES2015 examples using react-async-component. I also have react-hot-loader working with it. It took me a long time to get everything figured out so I hope it helps others.
Features are TypeScript or ES2015, Universal (SSR), React Hot Loader 3, React Router 4, Redux, Redux Saga, Redux Form, Async Component Code Splitting, Hapi, Webpack 3.
Webpack supports dynamic imports, which can have a variable in the module path.
To support this with react-async-component it must hold more than one loaded module, not just one.
This way it can prevent flash of content for each already loaded module.
Example for this is a bunch of possible icon files to load, but only know which to render at runtime.
In combination of that example and the dynamic import, another feature would be to use the loaded module not only as component.
I used another method render
to do something with the loaded module.
Here is an exapmle of the icon use case:
import React from 'react';
import GeneralIcon from 'place/GeneralIcon'
import { asyncComponent } from 'react-async-component';
export let config = {
resolve: (props) => import(`icons/${props.iconName}/iconDescription`),
getModuleId: (props) => props.iconName,
autoResolveES2015Default: false,
render: (iconDescription, props) => <GeneralIcon {...props} iconDescription={ iconDescription } />,
};
export default asyncComponent(config);
This can be used to
I created a working example, you can see the changes needed here.
@ctrlplusb please tell me if this looks good to you and I should proceed with it. I don't know much about SSR, so this is better done by you, if needs additional changes.
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.