React-router: getComponent is blocking whole render

Created on 6 May 2016  Â·  9Comments  Â·  Source: ReactTraining/react-router

Version

2.4.0

Steps to reproduce

I have a route config like below:

/* Router */
<ReactRouter history={ browserHistory }>
      <Redirect from="/" to="facebook" />
      <Route path="/">
          <Redirect from="facebook" to="facebook/dashboard" />
          { FacebookRoutes }
          <Route path="*" component={ NotFound } />
      </Route>
</ReactRouter>

/* FacebookRoutes */
function getDashboardComponent(nextState, callback) {
    setTimeout(() => {
        require.ensure([], require => {
            callback(null, require('path/to/component'));
        });
    }, 5000);
}

export default (
    <Route path="facebook" component={ Facebook }> //Facebook = some pure func wrapper component
        <Route path="dashboard" getComponent={ getDashboardComponent } />
    </Route>
);

Expected Behavior

Render the sync parts of the ui and then render the async route component when it's ready. In this case; i'm expecting that the Facebook component to be rendered before Dashboard component.

Actual Behavior

The async loading is blocking whole render and nothing is rendered until that async component's callback is called.

Most helpful comment

ok if you use babel 6 could you try the following require update :

/* FacebookRoutes */
function getDashboardComponent(nextState, callback) {
    setTimeout(() => {
        require.ensure([], require => {
-            callback(null, require('path/to/component'));
+            callback(null, require('path/to/component').default);
        });
    }, 5000);
}

There were some _under the hood_ changes in babel 6. Most are silent since we use the import keyword, and both ends of the 'pipe' are compatible.

But this async require.ensure relies on require instead of import, and now we have to precise.default, although in babel 5 it was working fine.

A lot of online docs are yet to be updated to babel6 new default behaviour.

I wait to hear from you if it could fix your issue.

All 9 comments

I might have a clue or be totally wrong...
Could you show me 'path/to/component' content please ?

@Kallikrein path/to/component:

import React from 'react';
import cx from 'classnames';
import styles from './style.scss';

const className = cx({
  [styles.Dashboard]: true,
});

export default function Dashboard() {
  return (
    <div className={ className }>
      Facebook Dashboard
    </div>
  );
}

ok if you use babel 6 could you try the following require update :

/* FacebookRoutes */
function getDashboardComponent(nextState, callback) {
    setTimeout(() => {
        require.ensure([], require => {
-            callback(null, require('path/to/component'));
+            callback(null, require('path/to/component').default);
        });
    }, 5000);
}

There were some _under the hood_ changes in babel 6. Most are silent since we use the import keyword, and both ends of the 'pipe' are compatible.

But this async require.ensure relies on require instead of import, and now we have to precise.default, although in babel 5 it was working fine.

A lot of online docs are yet to be updated to babel6 new default behaviour.

I wait to hear from you if it could fix your issue.

Thank you @Kallikrein
I've tried and it didn't work either. (.default is undefined) I think "babel-plugin-add-module-exports" is doing that for me.

Btw, i've solved my problem by using getChildRoutes as a splitting point. It's working as intended. The problem with the getComponent looks like a bug in router to me. I've tried to look into the source but couldn't find what's causing this.

This is intentional – getComponent is supposed to block the render, because there are no hooks there for loading state management, and we can't assume that your page can appropriately deal with children just being absent.

If you want to asynchronously fetch a component, given the current API, you'll want to do this in your route component itself.

Thank you @taion.

This is intentional – getComponent is supposed to block the render, because there are no hooks there for loading state management, and we can't assume that your page can appropriately deal with children just being absent.

Is this something that might change or be configurable in the future? I'm increasingly handling data which _might_ be loaded, and handling an incoming/pending route child would be a much better user experience than simply blocking.

@grrowl Then load that data in your standard React lifecycle methods if you want it to be async to the render process. You can also trigger side effects that will affect rendering while that getComponent call loads.

This is causing my grief. If the user navigates to a page, in my code the code chunk is loaded before the callback of getComponent is fired. If the user decides they don't want to wait and then navigates back to where they came from they still have to wait for that getComponent to resolve and then finally it loads the wrong component for the current url they are on, because they navigated back.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

maier-stefan picture maier-stefan  Â·  3Comments

jzimmek picture jzimmek  Â·  3Comments

yormi picture yormi  Â·  3Comments

nicolashery picture nicolashery  Â·  3Comments

hgezim picture hgezim  Â·  3Comments