Giter VIP home page Giter VIP logo

react-async-component's Issues

App hides some errors

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:
cryptit_155

but, if I use component without wrapping in asyncComponent, then I got an error in browser console, like this:
cryptit_156

Investigate possible solutions to React Hot Loader compatability

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

Is it possible to send props to the resolved component ?

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 in DashBoard, but its value is undefined.

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 ?

SSR + SEO: render on server but defer client loading

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.

Exception logging using Sentry.io

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?

HMR works Once then "is not accepted"

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.

Optimisations

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.

Combination with "fetchData" idea from Apollo GraphQL

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.

Add support for loading multiple imports per component (localization)

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?

Just to let you know it's pure magic!

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!

Pre resolve an asyncComponent when another component resolves

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

dynamic require in production

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") :

capture d ecran 2017-04-28 a 11 49 43

I wonder if dynamic require is handled by react-async-component ?

What am I doing wrong ?

Best regards

Issues with async resolve

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:

screen shot 2017-09-12 at 08 25 43

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.

How to write tests?

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?

[Discussion] Advanced use case

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

image

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.

Bake in some behaviour for module.hot reliability

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.

Component props in resolve function

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:

  • URL /my-very-nice-article corresponds ArticlePagecomponent
  • URL /statistics-for-my-other-very-nice-article corresponds ArticleStatisticsPage
    and etc.
    All matches between URLs and components are stored in database.

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.

Error Handling

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`.

Getting weird flashing when navigating

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?

Comparison with react-loadable

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 πŸ˜ƒ

Server side rendering with fetched data

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.

Async component local state not hydrated

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 :

  • Clone react-universally (master)
  • Edit shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js
  • Add a componentWillMount() which modify state (not predictable) like :
componentWillMount() {
        this.setState({counter: Math.random()});
  }

--> State is not rehydrated, react checksum errors appears etc.

Thank you !

setState Errors

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().

window object + naming bundles

Hi @ctrlplusb ! Nice library!

I am implementing it and it all works perfectly. Although I think I am not setting it up properly.

client

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

app

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

};

server

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.

LoadingComponent progress?

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

Cannot find module (on the server side)

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' }

Context is empty after async bootstrap

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)

Weird error when creating asyncComponent with router

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)

IE11 and Edge has error: Parsing error: Unexpected token import

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

Provide a synchronous version of "withAsyncComponents"

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.

React 16 Errors

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..

Possible Issues with withAsyncComponent

Thanks for putting this together.

Line 64

 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.

Line 103

 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.

How to call async component's internal function

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 πŸ˜„

Async component won't hot-reload

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?

Typescript type definitions

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

Preload dependencies during idle time?

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.

Nested AsyncComponents not resolved server-side

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.

Infinite Loops are common and easy to occur

If ANY problem happens then I get insanely fast errors going on. We need some sort of loop detection here to handle this. Not even sure why it happens - perhaps a way to solve it? Or am I just doing something wrong?

image

Document the "multiple componentWillMount" behaviour

Hey, thanks for your work on this.

I use componentWillMount to kick off some redux-thunks 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 componentWillMounts 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.

Consider Changing Double-Mount via prop?

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.

TypeScript & ES2015 Examples

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.

Support dynamic imports and arbitrary modules, not just components

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

  • apply module content to a generic component
  • bundling each file in a directory as its own bundle and decide at runtime by props which to show
  • loading one module which contain different parts of the app, like @djeeg showed here

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.

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.