├── .gitignore ├── not-now.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── readme.md ├── scripts └── build.js ├── src ├── App.js ├── Modal.js ├── StockChart.js ├── StockTable.js ├── index.css └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /not-now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "name": "react-lazy-preload-demo", 4 | "builds": [{ "src": "package.json", "use": "@now/static-build" }], 5 | "routes": [ 6 | { "src": "^/static/(.*)", "dest": "/static/$1" }, 7 | { "src": "/report", "dest": "/report.html" }, 8 | { "src": ".*", "dest": "/index.html" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazy-preload", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "moment": "^2.22.2", 7 | "react": "^16.6.1", 8 | "react-dom": "^16.6.1", 9 | "react-scripts": "2.1.1", 10 | "victory": "^30.6.0" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "node scripts/build", 15 | "now-build": "npm run build && mv build dist", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": [ 23 | ">0.2%", 24 | "not dead", 25 | "not ie <= 11", 26 | "not op_mini all" 27 | ], 28 | "devDependencies": { 29 | "webpack-bundle-analyzer": "^3.0.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomber/react-lazy-preload-demo/938502f89f00d7e97e193244c40f5ff7b48ea3be/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | Stocks App 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | ![alt text](https://cdn-images-1.medium.com/max/2000/1*O-tDkDg9bmDsnvX4vzhHkQ.png "Logo Title Text 1") 3 | ### [Lazy loading (and preloading) components in React 16.6](https://medium.com/@pomber/lazy-loading-and-preloading-components-in-react-16-6-804de091c82d) 4 | 5 | Versions (find the code in the Pull Request list): 6 | - [Not lazy](https://react-lazy.netlify.com/) 7 | - [Lazy, no preload](https://deploy-preview-2--react-lazy.netlify.com/) 8 | - [Lazy with preload](https://deploy-preview-8--react-lazy.netlify.com/) 9 | - [Lazy with prerender](https://deploy-preview-7--react-lazy.netlify.com/) 10 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = "production"; 2 | const BundleAnalyzerPlugin = require("webpack-bundle-analyzer") 3 | .BundleAnalyzerPlugin; 4 | 5 | const config = require("react-scripts/config/webpack.config.prod"); 6 | 7 | config.plugins.push( 8 | new BundleAnalyzerPlugin({ 9 | analyzerMode: "static", 10 | reportFilename: "report.html", 11 | defaultSizes: "gzip" 12 | }) 13 | ); 14 | 15 | config.optimization.runtimeChunk = false; 16 | config.optimization.splitChunks = { 17 | chunks() { 18 | return false; 19 | } 20 | }; 21 | 22 | require("react-scripts/scripts/build"); 23 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import StockTable from "./StockTable"; 3 | 4 | import StockChart from "./StockChart"; 5 | 6 | class App extends React.Component { 7 | state = { 8 | selectedStock: null 9 | }; 10 | render() { 11 | const { stocks } = this.props; 12 | const { selectedStock } = this.state; 13 | return ( 14 | 15 | this.setState({ selectedStock })} 18 | /> 19 | {selectedStock && ( 20 | this.setState({ selectedStock: false })} 23 | /> 24 | )} 25 | 26 | ); 27 | } 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /src/Modal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Modal = ({ children, onClose }) => ( 4 | 5 | {children} 6 | 7 | ); 8 | 9 | const Overlay = ({ children, onClose }) => ( 10 |
{ 25 | event.stopPropagation(); 26 | onClose(); 27 | }} 28 | /> 29 | ); 30 | 31 | const Content = ({ children, onClose }) => ( 32 |
{ 34 | event.stopPropagation(); 35 | }} 36 | style={{ 37 | background: "white", 38 | outline: "none", 39 | position: "relative", 40 | borderRadius: "10px" 41 | }} 42 | aria-modal="true" 43 | tabIndex="-1" 44 | > 45 | 46 | {children} 47 |
48 | ); 49 | 50 | const CloseButton = ({ onClick }) => ( 51 | 66 | ); 67 | 68 | export default Modal; 69 | -------------------------------------------------------------------------------- /src/StockChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | VictoryLine, 4 | VictoryChart, 5 | VictoryContainer, 6 | VictoryLabel, 7 | VictoryAxis 8 | } from "victory"; 9 | import Modal from "./Modal"; 10 | import moment from "moment"; 11 | 12 | export default function Chart({ stock, onClose }) { 13 | const [first, ...rest] = Object.values(stock.data); 14 | const [last] = rest.reverse(); 15 | const up = last.y >= first.y; 16 | const { width, height } = getDimentions(); 17 | return ( 18 | 19 | } 24 | > 25 | 32 | d.y} 35 | x={d => moment(d.x).format("MMM D")} 36 | style={{ 37 | labels: { opacity: 0.75 }, 38 | data: { stroke: up ? "darkgreen" : "darkred", strokeWidth: 3 } 39 | }} 40 | /> 41 | 47 | 48 | 49 | ); 50 | } 51 | 52 | function getDimentions() { 53 | const viewportWidth = Math.max( 54 | document.documentElement.clientWidth, 55 | window.innerWidth || 0 56 | ); 57 | const viewportHeight = Math.max( 58 | document.documentElement.clientHeight, 59 | window.innerHeight || 0 60 | ); 61 | 62 | const maxWidth = 600; 63 | const mobileViewportWidth = 500; 64 | let width = viewportWidth; 65 | width *= viewportWidth > mobileViewportWidth ? 0.9 : 1; 66 | width = Math.round(Math.min(width, maxWidth)); 67 | 68 | const maxHeight = viewportHeight * 0.95; 69 | let height = width / 1.62; 70 | height = Math.round(Math.min(height, maxHeight)); 71 | 72 | return { width, height }; 73 | } 74 | -------------------------------------------------------------------------------- /src/StockTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function StockTable({ stocks, onSelect }) { 4 | return ( 5 | 6 | 7 | 8 | {stocks.map(stock => ( 9 | onSelect(stock)} /> 10 | ))} 11 | 12 |
Stocks
13 | ); 14 | } 15 | 16 | function Row({ stock, onClick }) { 17 | return ( 18 | 19 | {stock.name} 20 | {stock.today} 21 | 22 | {stock.change < 0 ? stock.change : "+" + stock.change}% 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | font-size: 1.1rem; 10 | } 11 | 12 | table { 13 | border-collapse: collapse; 14 | } 15 | 16 | caption { 17 | font-size: 2rem; 18 | padding-bottom: 20px; 19 | } 20 | 21 | td:first-child { 22 | font-weight: bold; 23 | padding-right: 30px; 24 | padding-left: 5px; 25 | } 26 | 27 | tr td + td { 28 | text-align: right; 29 | padding: 8px 5px 8px 35px; 30 | } 31 | 32 | tr { 33 | cursor: pointer; 34 | padding: 0px 10px; 35 | } 36 | 37 | tr:hover { 38 | background: hsla(0, 0%, 0%, 0.075); 39 | } 40 | 41 | html, 42 | body, 43 | #root { 44 | height: 100%; 45 | } 46 | 47 | #root { 48 | display: flex; 49 | height: 100%; 50 | align-items: center; 51 | justify-content: center; 52 | } 53 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | function randomValue() { 7 | return Math.round(1000 + Math.random() * 1000) / 10; 8 | } 9 | 10 | function daysAgo(days) { 11 | return new Date(Date.now() - days * 24 * 60 * 60 * 1000); 12 | } 13 | 14 | function fakeStock(name) { 15 | const today = randomValue(); 16 | const yesterday = randomValue(); 17 | const change = Math.round((1000 * (today - yesterday)) / yesterday) / 10; 18 | return { 19 | name, 20 | today, 21 | change, 22 | data: [ 23 | { x: daysAgo(4), y: randomValue() }, 24 | { x: daysAgo(3), y: randomValue() }, 25 | { x: daysAgo(2), y: randomValue() }, 26 | { x: daysAgo(1), y: yesterday }, 27 | { x: daysAgo(0), y: today } 28 | ] 29 | }; 30 | } 31 | 32 | const stocks = [ 33 | fakeStock("Apple"), 34 | fakeStock("Citigroup"), 35 | fakeStock("General Electric"), 36 | fakeStock("Google"), 37 | fakeStock("Microsoft") 38 | ]; 39 | 40 | ReactDOM.render(, document.getElementById("root")); 41 | --------------------------------------------------------------------------------