Hi!
I'm trying to implement code splitting based on router's dynamic feature(https://github.com/rackt/react-router/blob/master/docs/guides/advanced/DynamicRouting.md) like that
path: 'course/:courseId',
getChildRoutes(location, callback) {
require.ensure([], function (require) {
callback(null, [
require('./routes/Announcements'),
require('./routes/Assignments'),
require('./routes/Grades'),
])
})
},
getIndexRoute(location, callback) {
require.ensure([], function (require) {
callback(null, {
component: require('./components/Index'),
})
})
},
getComponents(location, callback) {
require.ensure([], function (require) {
callback(null, require('./components/Course'))
})
}
It works fine in this commit(just react-router and basic server render).
https://github.com/EcutDavid/example-react-router-server-rendering-lazy-routes/commit/6e070ee4a573fefe730dec2eadbe6dc38d0e5529
But it not works on my application based on this project.

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method ofRoutingContext.
Currently my code like that:
-routes
--index.js
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
export default {
component: 'div',
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./ShareGraphRoute'),
require('./AppRoute'),
require('./NotFoundRoute'),
])
})
}
}
-routes
--ShareGraphRoute
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
import {
SHARE_GRAPH_PATH,
} from 'constants/defaults'
export default {
path: SHARE_GRAPH_PATH,
getComponent(location, cb) {
require.ensure([], (require) => {
cb(null, require('components/ShareGraph'))
})
},
}
Seems need change in server.js or I made some mistakes, any hint?
thanks!!
@EcutDavid have you got polifyll in your first file? What babel are you using? If you are using babel6, you need to specify default to use default export in require like this:
...
require('./routes/Announcements').default,
...
is your source hosted somewhere? I can look and help you :)
@svrcekmichal Thanks!
it's not caused by no use .default.
Sorry, it's company's private project, can not display the whole application code....
But it's build based on this repo.
Here is the dependencies: use babel 5.8
"dependencies": {
"babel-core": "5.8.x",
"babel-runtime": "^5.8.34",
"bluebird": "2.10.2",
"classnames": "^2.1.2",
"compression": "^1.6.0",
"d3": "3.5.6",
"es6-promise": "3.0.2",
"express": "^4.13.3",
"falcor": "^0.1.13",
"falcor-express": "^0.1.2",
"falcor-http-datasource": "^0.1.2",
"falcor-router": "^0.2.9",
"geoip-lite-country": "^1.1.3",
"history": "1.17.0",
"hoist-non-react-statics": "^1.0.3",
"http-server": "^0.7.5",
"immutable": "^3.7.4",
"invariant": "^2.2.0",
"json-loader": "^0.5.2",
"leaflet": "^0.7.5",
"lodash": "^3.10.1",
"mapbox.js": "^2.2.2",
"match-media": "^0.2.0",
"numeral": "^1.5.3",
"piping": "^0.3.0",
"pretty-error": "^1.2.0",
"query-string": "^2.4.2",
"ramda": "^0.18.0",
"react": "^0.14.2",
"react-addons-css-transition-group": "^0.14.3",
"react-addons-test-utils": "^0.14.3",
"react-autolink-text": "wiredcraft/react-autolink-text",
"react-dom": "^0.14.2",
"react-immutable-proptypes": "^1.2.3",
"react-infinite": "^0.7.3",
"react-progress": "0.0.9",
"react-redux": "^4.0.0",
"react-router": "^1.0.2",
"react-slider": "^0.6.0",
"redis": "2.2.5",
"redux": "^3.0.4",
"redux-falcor": "^2.3.3",
"redux-router": "^1.0.0-beta5",
"redux-thunk": "^0.1.0",
"reselect": "^2.0.0",
"rx": "3.1.1",
"serialize-javascript": "^1.1.2",
"serve-favicon": "^2.3.0",
"serve-static": "^1.10.0",
"topojson": "^1.6.19",
"transit-js": "^0.8.837",
"warning": "^2.1.0",
"webpack-isomorphic-tools": "^0.9.3"
},
"devDependencies": {
"align-text": "^0.1.3",
"autoprefixer-loader": "^3.1.0",
"babel-eslint": "4.1.x",
"babel-loader": "^5.4.0",
"babel-plugin-react-transform": "^1.1.1",
"balanced-match": "^0.2.0",
"benchpress": "2.0.0-alpha.46",
"better-npm-run": "0.0.2",
"concat-map": "0.0.1",
"concurrently": "^0.1.1",
"css-loader": "^0.23.0",
"cssnext-loader": "^1.0.1",
"eslint": "^1.10.2",
"eslint-plugin-react": "^3.11.1",
"extract-text-webpack-plugin": "^0.8.2",
"file-loader": "^0.8.5",
"fs-extra": "^0.22.1",
"gh-pages": "0.3.1",
"glob": "^5.0.14",
"jasmine": "^2.3.1",
"node-bourbon": "^4.2.3",
"node-sass": "^3.4.2",
"normalize.css": "3.0.3",
"phantomjs": "^1.9.18",
"phantomjs-polyfill": "0.0.1",
"protractor": "3.0.0",
"react-hot-loader": "^1.3.0",
"react-transform-hmr": "^1.0.1",
"redux-devtools": "^2.1.5",
"reflect-metadata": "^0.1.0",
"sass-loader": "^3.1.2",
"strip-loader": "^0.1.0",
"style-loader": "0.12.3",
"traceur": "0.0.91",
"url-loader": "^0.5.7",
"webpack": "^1.12.9",
"webpack-dev-middleware": "^1.4.0",
"webpack-hot-middleware": "^2.5.0",
"yargs": "^3.17.1"
}
The error prompted after change below:
+// polyfill webpack require.ensure
+if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
+
import {
SHARE_GRAPH_PATH,
} from 'constants/defaults'
-import ShareGraph from 'components/ShareGraph'
export default {
path: SHARE_GRAPH_PATH,
- component: ShareGraph
+ getComponent(location, cb) {
+ require.ensure([], (require) => {
+ cb(null, require('components/ShareGraph'))
+ })
+ },
}
But
-routes
--index.js mentioned before, works fine.
@EcutDavid what is output of this?
console.log(require('components/ShareGraph'))
can you compare it with original import?
Here is the output:
{ __esModule: true,
[1] ShareGraph:
[1] { [Function: ShareGraph]
[1] propTypes:
[1] { chart: [Function: bound checkType],
[1] location: [Function: bound checkType],
[1] selectedIndicator: [Object],
[1] selectedView: [Function: bound checkType],
[1] switchUserCountry: [Function: bound checkType],
[1] title: [Function: bound checkType],
[1] updateSelectionByQuery: [Function: bound checkType] } },
[1] default:
[1] { [Function: Connect]
[1] displayName: 'Connect(ShareGraph)',
[1] WrappedComponent: { [Function: ShareGraph] propTypes: [Object] },
[1] contextTypes: { store: [Object] },
[1] propTypes: { store: [Object] } } }
after see the output, I change cb(null, require('components/ShareGraph')) to cb(null, require('components/ShareGraph').default)
It works!!!!
Why there is no need to add .default in there???
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
export default {
component: 'div',
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./ShareGraphRoute'),
require('./AppRoute'),
require('./NotFoundRoute'),
])
})
}
}
@svrcekmichal
Thank you very much!
Could you give me some reference about when to use the default and when not???
Thanks again!
@EcutDavid well, you need to use .default when you need to use old require with es6 default exported content. So it really depends on type of export you use, I can't tell you why the second require works, but you can always try console.log to see output of require.
Or simply paste here source of ShareGraphRoute and ShareGraph component and console.log of their require, so we can see the difference
ShareGraph
/* global __CLIENT__ */
import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import numeral from 'numeral'
import _ from 'lodash'
import * as RouteActions from 'actions/RouteActions'
import * as UserActions from 'actions/UserActions'
import {
APP_PATH,
VIEW_CHART,
VIEW_MAP,
} from 'constants/defaults'
import * as propTypes from 'constants/propTypes'
import {
indicatorChartSelector,
locationsChartSelector,
} from 'selectors/dataSelectors'
import {
selectedFeaturedIndicatorMetaSelector,
selectedIndicatorMetaSelector,
selectedIndicatorTypeSelector,
} from 'selectors/indicatorSelectors'
import MapComponent from './MapComponent'
import Chart from './Chart'
import { wbNormal } from 'constants/textFormat'
export class ShareGraph extends Component {
static propTypes = {
chart: propTypes.chart.isRequired,
location: PropTypes.object.isRequired,
selectedIndicator: PropTypes.string,
selectedView: PropTypes.string.isRequired,
switchUserCountry: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
updateSelectionByQuery: PropTypes.func.isRequired,
}
componentWillMount() {
const { location, updateSelectionByQuery, switchUserCountry } = this.props
updateSelectionByQuery({}, location.query)
if (__CLIENT__) {
const xhr = new XMLHttpRequest()
xhr.open('GET', '/geo/ip', true)
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
switchUserCountry(JSON.parse(xhr.response).country)
} else {
console.error(JSON.parse(xhr.response).error)
}
}
}
xhr.onerror = function (e) {
console.error('onerror', xhr.statusText)
}
xhr.send()
}
numeral.language('wbNormal', wbNormal)
numeral.language('wbNormal')
}
render() {
const {
chart: { data, domain },
location,
selectedIndicator,
selectedView,
title,
} = this.props
const onlyLocations = !selectedIndicator
const url = APP_PATH + location.search
return (
<article className='card indicator iframe'>
<h1>{ title }</h1>
{ (selectedView === VIEW_CHART || onlyLocations) && (
<Chart
data={data}
domain={domain}
onSelect={() => {}}
options={{ hideSpinner: true }}
/>
)}
{ (!onlyLocations && selectedView === VIEW_MAP) && (
<MapComponent />
)}
<span className='footer'>
Data from <a href={url} target='_blank'>World Bank</a>
</span>
</article>
)
}
}
function mapStateToProps (state, props) {
const { view, router } = state
const onlyLocationsSelected = props.location.query.indicators === undefined
const chart = onlyLocationsSelected ?
locationsChartSelector(state) :
indicatorChartSelector(state)
const indicator = onlyLocationsSelected ?
selectedFeaturedIndicatorMetaSelector(state) :
selectedIndicatorMetaSelector(state)
return {
chart,
selectedView: view.get('selectedView'),
location: router.location,
selectedIndicator: selectedIndicatorTypeSelector(state),
title: _.get(indicator, 'fullname', ''),
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
updateSelectionByQuery: RouteActions.updateSelectionByQuery,
switchUserCountry: UserActions.switchUserCountry,
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ShareGraph)
ShareGraphRoute
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
import {
SHARE_GRAPH_PATH,
} from 'constants/defaults'
export default {
path: SHARE_GRAPH_PATH,
getComponent(location, cb) {
require.ensure([], (require) => {
cb(null, require('components/ShareGraph').default)
}, 'ShareGraph')
},
}
looks like the same...
@EcutDavid and console.log of their requires?
@EcutDavid from what i can tell ShareGraphRoute will be exported as object, with keys
default,path and getComponent which can work, because you have path and getcomponent available. But in first ShareGraph you only export default and ShareGraph, but router don't know how to use this object, it's not react component
Route:
{ path: '/share/widget',
getComponent: [Function: getComponent] }
Component:
{ __esModule: true,
[1] ShareGraph:
[1] { [Function: ShareGraph]
[1] propTypes:
[1] { chart: [Function: bound checkType],
[1] location: [Function: bound checkType],
[1] selectedIndicator: [Object],
[1] selectedView: [Function: bound checkType],
[1] switchUserCountry: [Function: bound checkType],
[1] title: [Function: bound checkType],
[1] updateSelectionByQuery: [Function: bound checkType] } },
[1] default:
[1] { [Function: Connect]
[1] displayName: 'Connect(ShareGraph)',
[1] WrappedComponent: { [Function: ShareGraph] propTypes: [Object] },
[1] contextTypes: { store: [Object] },
[1] propTypes: { store: [Object] } } }
@svrcekmichal
The component generate a filed __esModule
So there it is, your component exported ShareGraph and default and you have to tell router, which of them to use, the overall export is not react component
Got it, that's make sense.
@svrcekmichal you help me a lot, thanks!
You are welcome :)
Thanks, this helped me track down a bug that was ruining my day!!
Most helpful comment
You are welcome :)