-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add code splitting support to RootElement (#963)
* Added support for componentLoader on RootElement * Added code splitting example * Fixed isse where client transitions with reuseDom enabled was broken
- Loading branch information
Showing
16 changed files
with
4,393 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"presets": ["react-server"], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
__clientTemp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"routesFile": "./routes.js", | ||
"webpackConfig": "./webpackConfig.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Basic Code Splitting Example | ||
|
||
A version of the HelloWorld page with code splitting enabled. This page has 3 parts to it, header, body, and a footer. The body is configured to be loaded on initial page load. The header will then load soon after that and with the footer loading around 6 seconds after the inital page was loaded. | ||
|
||
This demo currently utilizes `require.ensure` to power dynamic code splitting. But in the future this can be done with `import()`. | ||
|
||
To install: | ||
|
||
``` | ||
git clone https://github.com/redfin/react-server.git | ||
cd react-server/packages/react-server-examples/code-splitting | ||
npm install | ||
``` | ||
|
||
To start in development mode: | ||
|
||
``` | ||
npm start | ||
``` | ||
|
||
Then go to [localhost:3000](http://localhost:3000/). You will see a simple page that pre-renders and that is interactive on load. It also will include hot reloading of React components in their own file. | ||
|
||
# How to do Code Splitting | ||
`RootElement` will take an async function via the `componentLoader` prop. This function is expected to resolve to a single React component that `RootElement` will then render and pass any props from `listen`, `when`, and/or `childProps` into. | ||
|
||
# Example Usages | ||
|
||
This will render the component that calling `loader` resolves to. | ||
```jsx | ||
getElements() { | ||
return ( | ||
<RootElement componentLoader={loader} /> | ||
); | ||
} | ||
``` | ||
|
||
This will render the component that calling `loader` resolves to and with `displayText` passed in as a prop to it. | ||
```jsx | ||
getElements() { | ||
return ( | ||
<RootElement componentLoader={loader} childProps={{displayText: "Hello World"}}/> | ||
); | ||
} | ||
``` | ||
|
||
This will render when `storeEmitter` has fired at least once and `loader` has resolved to a component. Any subsequent fires from `storeEmitter` will trigger a re-render with updated prop values. | ||
```jsx | ||
getElements() { | ||
return ( | ||
<RootElement listen={storeEmitter} componentLoader={loader} /> | ||
); | ||
} | ||
``` | ||
|
||
This will render the resolved component with `Content` as a child of the resolved component. | ||
```jsx | ||
getElements() { | ||
return ( | ||
<RootElement componentLoader={loader}> | ||
<Content text="Hello World" /> | ||
</RootElement> | ||
); | ||
} | ||
``` |
27 changes: 27 additions & 0 deletions
27
packages/react-server-examples/code-splitting/componentLoaders/FooterLoader.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Q from "q"; | ||
|
||
const _loadFooterCode = (deferred) => { | ||
require.ensure([], function() { | ||
const component = require("../components/Footer").default; | ||
deferred.resolve(component); | ||
}, "Footer"); | ||
} | ||
|
||
// This will load the footer code when called | ||
// This loader is configured to wait 6 seconds after it was called | ||
// before it fires off the network request to load the chunk | ||
const footerLoader = () => { | ||
const deferred = Q.defer(); | ||
|
||
// eslint-disable-next-line no-process-env | ||
if (process.env.IS_SERVER) { | ||
const component = require("../components/Footer").default; | ||
deferred.resolve(component); | ||
} else { | ||
setTimeout(_loadFooterCode.bind(null, deferred), 6000); | ||
} | ||
|
||
return deferred.promise; | ||
}; | ||
|
||
export default footerLoader; |
27 changes: 27 additions & 0 deletions
27
packages/react-server-examples/code-splitting/componentLoaders/HeaderLoader.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Q from "q"; | ||
|
||
// This loads the header code | ||
// This loader is configured to fire off the request for the chunk | ||
// as soon as the loader is called | ||
const headerLoader = () => { | ||
const deferred = Q.defer(); | ||
|
||
// We need this IS_SERVER check because require.ensure is not supported | ||
// on the server. There we just use regular require to get | ||
// the component | ||
|
||
// eslint-disable-next-line no-process-env | ||
if (process.env.IS_SERVER) { | ||
const component = require("../components/Header").default; | ||
deferred.resolve(component); | ||
} else { | ||
require.ensure([], function() { | ||
const component = require("../components/Header").default; | ||
deferred.resolve(component); | ||
}, "Header"); | ||
} | ||
|
||
return deferred.promise; | ||
}; | ||
|
||
export default headerLoader; |
30 changes: 30 additions & 0 deletions
30
packages/react-server-examples/code-splitting/components/Body.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React, { Component } from 'react'; | ||
import {logging} from 'react-server'; | ||
|
||
const logger = logging.getLogger(__LOGGER__); | ||
|
||
export default class Body extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.increment = () => { | ||
this.setState({exclamationCount: this.state.exclamationCount + 1}); | ||
} | ||
|
||
this.state = { | ||
exclamationCount: 0, | ||
}; | ||
} | ||
|
||
componentDidMount() { | ||
logger.info("body rendered"); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div className="body"> | ||
<h2>Hello, World{"!".repeat(this.state.exclamationCount)}</h2> | ||
<button onClick={this.increment}>Get More Excited!</button> | ||
</div> | ||
); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
packages/react-server-examples/code-splitting/components/Footer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React, { Component } from 'react'; | ||
import {logging} from 'react-server'; | ||
|
||
const logger = logging.getLogger(__LOGGER__); | ||
|
||
export default class Footer extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
mounted: false, | ||
}; | ||
} | ||
|
||
componentDidMount() { | ||
logger.info("footer rendered"); | ||
this.setState({ | ||
mounted: true, | ||
}); | ||
} | ||
|
||
render() { | ||
const isMounted = this.state.mounted; | ||
const footerText = `Footer ${isMounted ? "is loaded" : " has not been loaded"}`; | ||
return ( | ||
<div className="footer"> | ||
{footerText} | ||
</div> | ||
); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
packages/react-server-examples/code-splitting/components/Header.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import {logging} from 'react-server'; | ||
|
||
const logger = logging.getLogger(__LOGGER__); | ||
|
||
export default ({ headerText }) => { | ||
logger.info("rendering the header"); | ||
|
||
return ( | ||
<div className="header"> | ||
React-Server { headerText } | ||
</div> | ||
); | ||
} |
24 changes: 24 additions & 0 deletions
24
packages/react-server-examples/code-splitting/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "react-server-examples-code-splitting", | ||
"private": true, | ||
"version": "0.0.1", | ||
"description": "", | ||
"main": "HelloWorld.js", | ||
"scripts": { | ||
"start": "IS_SERVER=true react-server start", | ||
"clean": "rm -rf build __clientTemp", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "David Kuang", | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"q": "^2.0.3", | ||
"react": "^15.4.1", | ||
"react-dom": "15.4.1", | ||
"react-server": "^0.6.3", | ||
"react-server-cli": "^0.6.3" | ||
}, | ||
"devDependencies": { | ||
"babel-preset-react-server": "^0.6.0" | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
packages/react-server-examples/code-splitting/pages/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from "react" | ||
import { RootElement } from "react-server"; | ||
import Body from "../components/Body"; | ||
|
||
// These are async functions that resolve to a single React component | ||
import HeaderLoader from "../componentLoaders/HeaderLoader"; | ||
import FooterLoader from "../componentLoaders/FooterLoader"; | ||
|
||
export default class CodeSplittingPage { | ||
getTitle() { | ||
return "Code Splitting Demo"; | ||
} | ||
|
||
getElements () { | ||
return [ | ||
// childProps will be passed into the loaded component | ||
<RootElement componentLoader={HeaderLoader} childProps={{ headerText: "Code Splitting Demo" }}/>, | ||
<Body />, | ||
<RootElement componentLoader={FooterLoader} />, | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
routes: { | ||
Index: { | ||
path: ['/'], | ||
method: 'get', | ||
page: "./pages/index", | ||
}, | ||
}, | ||
}; |
17 changes: 17 additions & 0 deletions
17
packages/react-server-examples/code-splitting/webpackConfig.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import webpack from "webpack"; | ||
|
||
export default (webpackConfig) => { | ||
|
||
// Optional. This makes it so the child chunks receive a readable name versus | ||
// just the chunk id | ||
webpackConfig.output.chunkFilename = "[name]." + webpackConfig.output.chunkFilename; | ||
|
||
// This is to prevent webpack from running the code in these blocks | ||
// Needed because otherwise webpack will bundle in the async components | ||
// into the main page bundle | ||
webpackConfig.plugins.push(new webpack.DefinePlugin({ | ||
"process.env.IS_SERVER": 'false', | ||
})); | ||
|
||
return webpackConfig; | ||
}; |
Oops, something went wrong.