Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Webpack's async code splitting with React Router
'use strict';
var React = require('react');
function createAsyncHandler(getHandlerAsync, displayName) {
var Handler = null;
return React.createClass({
displayName: displayName,
statics: {
willTransitionTo(transition, params, query, callback) {
getHandlerAsync().then(resolvedHandler => {
Handler = resolvedHandler;
if (!Handler.willTransitionTo) {
return callback();
}
Handler.willTransitionTo(transition, params, query, callback);
if (Handler.willTransitionTo.length < 4) {
callback();
}
});
},
willTransitionFrom(transition, component, callback) {
if (!Handler || !Handler.willTransitionFrom) {
callback();
}
Handler.willTransitionFrom(transition, component, callback);
if (Handler.willTransitionFrom.length < 3) {
callback();
}
}
},
render() {
return <Handler {...this.props} />;
}
});
}
module.exports = createAsyncHandler;
@geekyme
Copy link

geekyme commented Feb 2, 2015

Any usage examples?

@gaearon
Copy link
Author

gaearon commented Feb 2, 2015

var loadSomePage = require('promise?global!./pages/SomePage'),
    createAsyncHandler = require('./utils/createAsyncHandler');

...

<Route name='somePage' path='/something' handler={createAsyncHandler(loadSomePage)}

Where promise refers to promise-loader (https://github.com/gaearon/promise-loader).

@geekyme
Copy link

geekyme commented Feb 2, 2015

ah cool! thank you! I suppose to make this work on the server side then we can use some feature flags to require modules synchronously. https://github.com/petehunt/webpack-howto#6-feature-flags

if(IS_CLIENT){
    var loadSomePage = require('promise?global!./pages/SomePage'),
        createAsyncHandler = require('./utils/createAsyncHandler'),
        handler = createAsyncHandler(loadSomePage)
} else {
    var handler = require('./pages/SomePage');
}

...
<Route name='somePage' path='/something' handler={createAsyncHandler(loadSomePage)}

@Retired
Copy link

Retired commented Feb 6, 2015

Sorry, this is probably a novice question but does this gist need to be run through some specific loader?

I am using it in the following manner and webpack seems to be having issues parsing the module.

var createAsyncHandler = require("./utils/createAsyncHandler.jsx");
var SomeView = require("promise?bluebird!./components/SomeView.jsx");

<Route name=“some”View path=“lazyView” handler={createAsyncHandler(SomeView)} />

Issues:

Line 12: Unexpected token (
You may need an appropriate loader to handle this file type.
| 
|     statics: {
|       willTransitionTo(transition, params, query, callback) {
|         getHandlerAsync().then(resolvedHandler => {
|           Handler = resolvedHandler;

I tried replacing the top-level function calls with what I assume are properties for the static object (please let me know if I'm incorrect; I'm fairly new to JavaScript and there are at least 3 constructs in this gist that I have not seen before) but then webpack complains about the => operator.

Line 16: Unexpected token =>
You may need an appropriate loader to handle this file type.
|       statics: {
|           willTransitionTo: function(transition, params, query, callback) {
|               someHandler().then(resolvedHandler => {
|                   Handler = resolvedHandler;
| 

I have tried replace the resolvedHandler => with function(resolvedHandler) which allowed webpack to bundle everything but ultimately, that led to some TypeErrors at runtime, which makes me think that I must be missing something.

I am running all jsx files through the jsx-loader.

loaders: [
    { test: /\.jsx$/, loader: "jsx-loader" }
]

If it's of any relevance, I have installed react, react-router, bluebird, and promise-loader via npm install.

@Schniz
Copy link

Schniz commented Feb 6, 2015

@Retired, try run the jsx files with jsx-loader?harmony instead of just jsx-loader. it works for me

@Retired
Copy link

Retired commented Feb 11, 2015

@Schniz, thank you! Apologies for the delayed response but that seemed to have solved the problem. I guess those unfamiliar constructs in gaearon's gist belong to the ES6 spec.

@pilwon
Copy link

pilwon commented Feb 17, 2015

@gaearon Thanks for sharing the code. Line 29 is missing return.

@mikechau
Copy link

mikechau commented Feb 19, 2015

@gaearon this is awesome, thank you!

@plasticine
Copy link

plasticine commented Feb 26, 2015

This is pretty neat, but does have some unfortunate side-effects.

For example the component param that will be passed to willTransitionFrom will reference the component returned from createAsyncHandler — not Handler. This isn’t a deal-breaker but does mean that this isn’t fool-proof.

@plasticine
Copy link

plasticine commented Feb 26, 2015

Which I guess you can hack around if you’re happy to reach in and grab a reference to the component when it renders. Pretty shady though. :/

componentDidMount: {
  if (Handler) {
    HandlerComponent = this.refs.component;
  }
}
Handler.willTransitionFrom(transition, HandlerComponent, callback)

@makefunstuff
Copy link

makefunstuff commented Apr 7, 2015

Is it possible to use this example with es6 harmony modules?

@jamesjjk
Copy link

jamesjjk commented Jul 31, 2015

Have you tried with isomorphic ensure? : https://github.com/tomekwi/isomorphic-ensure

@rubencodes
Copy link

rubencodes commented Jan 21, 2016

Any chance this gist can be updated for latest RR? Tried doing it myself but wasn't able to do it...The willTransitionTo/From hooks aren't a thing anymore, I think.

@anorudes
Copy link

anorudes commented Mar 7, 2016

+1, can you update the code?)

@rosskevin
Copy link

rosskevin commented May 25, 2016

I just researched a bunch of this, and settled on https://github.com/luqin/react-router-loader. It has considered several other forks of loaders and appears to be maintained.

Here's a gist of the dynamic auth-flow example using jsx syntax:
https://gist.github.com/rosskevin/de55079e0d48f046fb518e45bb4f12b1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment