Skip to content

Commit

Permalink
Updated example app (server-side rendering)
Browse files Browse the repository at this point in the history
  • Loading branch information
gragland committed Dec 14, 2016
1 parent 399de6c commit 6efc030
Show file tree
Hide file tree
Showing 22 changed files with 376 additions and 155 deletions.
2 changes: 1 addition & 1 deletion dist/instatype.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions examples/unsplash/index.html
@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/assets/style.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<link rel='stylesheet' type='text/css' href='/assets/style.css'/>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'/>
</head>
<body>
<div id="app"></div>
<script src="/assets/bundle.js"></script>
<div id='app'></div>
<script src='/assets/bundle.js'></script>
</body>
</html>
16 changes: 11 additions & 5 deletions examples/unsplash/package.json
@@ -1,11 +1,12 @@
{
"description": "Example app using instatype",
"scripts": {
"build": "NODE_ENV=production webpack --config webpack.config.js",
"build-dev": "NODE_ENV=development webpack --config webpack.config.js",
"start": "NODE_ENV=production node server.js",
"start-dev": "NODE_ENV=development node server.js",
"start-dev-dual": "NODE_ENV=development DUAL=1 node server.js",
"start:dev": "NODE_ENV=development node server.js",
"start:prod": "NODE_ENV=production node server.js",
"start:prod:iso": "NODE_ENV=production node server-build/server.js",
"build:dev": "NODE_ENV=development webpack --config webpack.config.js",
"build:prod": "NODE_ENV=production webpack --config webpack.config.js",
"build:prod:iso": "npm run build:prod && NODE_ENV=production BUILD_SERVER=1 webpack --config webpack.config.js",
"now-deploy": "npm run build && now -e NODE_ENV=production",
"now-build": ""
},
Expand All @@ -14,6 +15,7 @@
"node": ">=6.9.0"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-core": "6.18.2",
"babel-loader": "6.2.8",
"babel-plugin-transform-async-to-generator": "^6.16.0",
Expand All @@ -24,6 +26,7 @@
"babel-preset-react": "6.16.0",
"react-hot-loader": "^3.0.0-beta.0",
"react-render-visualizer-decorator": "^0.3.0",
"url-loader": "^0.5.7",
"webpack": "1.13.3",
"webpack-dev-middleware": "^1.6.1",
"webpack-hot-middleware": "^2.10.0"
Expand All @@ -36,9 +39,12 @@
"lodash": "^4.17.2",
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-helmet": "^3.2.3",
"react-router": "^3.0.0",
"react-scroll-up": "^1.1.5",
"react-simple-grid": "^1.1.0",
"unsplash-js": "^4.3.1",
"webpack-node-externals": "^1.5.4",
"whatwg-fetch": "^2.0.1"
}
}
22 changes: 11 additions & 11 deletions examples/unsplash/public/assets/bundle.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions examples/unsplash/server-build/server.js

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions examples/unsplash/server-isomorphic.js
@@ -0,0 +1,70 @@
import path from 'path';
import express from 'express';
import compression from 'compression';
import React from 'react';
import { renderToStaticMarkup, renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import Helmet from 'react-helmet';
import routes from './src/routes.js';
import Layout from './src/components/Layout.js';
import DataWrapper from './src/components/DataWrapper.js';
import api from './src/api.js';

// Fetch data based on route (path, params) here
async function fetchData(path, params){
if (path === '/'){
const photos = await api.getPopularPhotos(1);
var serverData = { path: path, data: photos };
return serverData;
}
}

var server = express();

server
.use(compression())
.use('/assets', express.static(path.join(__dirname, './../public/assets'), { index: false, maxAge: 31536000000 }))
.use((req, res, next) => {
match(
{ routes: routes, location: req.url },
async (error, redirectLocation, renderProps) => {

if (error){
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(301, redirectLocation.pathname + redirectLocation.search);
} else if (!renderProps) { // Should never happen if we have a catch-all (*) error page route
res.status(404).send('Page not found');
} else {

const serverData = await fetchData(req.url, renderProps.params);

// Render route as a string
var markup = renderToString(
<DataWrapper serverData={serverData}>
<RouterContext {...renderProps} />
</DataWrapper>
);

// Extract data for page <head> (title, meta, scripts)
// See: https://github.com/nfl/react-helmet#server-usage
var head = Helmet.rewind();

// Render layout and pass in markup, serverData fetched server-side, and head
// We use renderToStaticMarkup for layout so react-id dom attributes aren't added
res.send(
renderToStaticMarkup( <Layout markup={markup} serverData={serverData} head={head} /> )
);

}
}
);
})
.listen( (process.env.PORT || 3000), (err, result) => {
if (err) {
console.log(err);
}
console.log('Server started');
});

module.exports = server
1 change: 0 additions & 1 deletion examples/unsplash/server.js
Expand Up @@ -22,7 +22,6 @@ if (process.env.NODE_ENV === 'development'){
server.use(compression());
}


server.use('/assets', express.static(__dirname + '/public/assets'));

server.get('*', function(req, res) {
Expand Down
26 changes: 23 additions & 3 deletions examples/unsplash/src/App.js
Expand Up @@ -26,8 +26,21 @@ class App extends React.PureComponent {
this.getPage = this.getPage.bind(this);
}

componentDidMount(){
this.getPage();
componentWillMount(){

const { serverData } = this.context;

if (serverData && serverData.path === '/'){

this.setState({
section: 'popular',
photos: serverData.data,
page: 2
});

}else{
this.getPage();
}
}

async getPage(){
Expand Down Expand Up @@ -116,7 +129,9 @@ class App extends React.PureComponent {
ref='instatype'/>
</div>


<div style={{ marginBottom: '30px' }}>

<Grid spacing={5} breakPoints={[ { maxWidth: 800, spacing: 1, blockWidth: [ 1/4 ] } ]} hideOuterSpacing={true} blockWidth={[ 1/3, 2/3, 2/3, 1/3 ]}>

<Block><div style={{ backgroundColor: 'red', width: '100%', height: '100px' }}></div></Block>
Expand All @@ -130,7 +145,8 @@ class App extends React.PureComponent {

</Grid>
</div>



{ photos && photos.length > 0 &&
<Infinite requestHandler={this.getPage} atEnd={atEnd}>
<Grid blocksPerRow={4} spacing={5} breakPoints={photoGridBreakPoints} passBlockWidth={true} hideOuterSpacing={true}>
Expand All @@ -157,4 +173,8 @@ class App extends React.PureComponent {
}
};

App.contextTypes = {
serverData: React.PropTypes.object
};

export default App;
15 changes: 15 additions & 0 deletions examples/unsplash/src/components/404.js
@@ -0,0 +1,15 @@
var React = require('react');
var Helmet = require('react-helmet');

var NotFoundComponent = React.createClass({
render(){
return (
<div>
<Helmet title="Page not found" />
Oops.. That page could not be found.
</div>
);
}
});

module.exports = NotFoundComponent;
20 changes: 20 additions & 0 deletions examples/unsplash/src/components/DataWrapper.js
@@ -0,0 +1,20 @@
import React from 'react';

class DataWrapper extends React.PureComponent {

getChildContext () {
return {
serverData: this.props.serverData
};
}

render(){
return this.props.children;
}
};

DataWrapper.childContextTypes = {
serverData: React.PropTypes.object
};

export default DataWrapper;
12 changes: 7 additions & 5 deletions examples/unsplash/src/components/Image/Image.js
@@ -1,7 +1,7 @@
import React from 'react';
import Block from './ImageBlock';

const Image = ({ src, heightWidthRatio, parseSrc, parseSrcWidth, parseSrcAllowedWidths, parseSrcDoubleForRetina, children, ...props }) => {
const Image = ({ src, widthHeightRatio, parseSrc, parseSrcWidth, parseSrcAllowedWidths, parseSrcDoubleForRetina, children, ...props }) => {

/** If src parsing is enabled ...
* We replace width and height in src url
Expand All @@ -26,7 +26,7 @@ const Image = ({ src, heightWidthRatio, parseSrc, parseSrcWidth, parseSrcAllowed
}

src = src.replace(/\{width\}/g, parseInt(parseSrcWidth))
src = src.replace(/\{height\}/g, parseInt(parseSrcWidth * heightWidthRatio));
src = src.replace(/\{height\}/g, parseInt(parseSrcWidth / widthHeightRatio));
}

const style = {
Expand All @@ -37,7 +37,7 @@ const Image = ({ src, heightWidthRatio, parseSrc, parseSrcWidth, parseSrcAllowed
};

return (
<Block heightWidthRatio={heightWidthRatio}>
<Block widthHeightRatio={widthHeightRatio}>
{ (!parseSrc || parseSrcWidth) &&
<img src={src} style={style} {...props} />
}
Expand All @@ -52,7 +52,7 @@ const Image = ({ src, heightWidthRatio, parseSrc, parseSrcWidth, parseSrcAllowed

Image.propTypes = {
src: React.PropTypes.string.isRequired,
heightWidthRatio: React.PropTypes.number,
widthHeightRatio: React.PropTypes.number,
parseSrc: React.PropTypes.bool,
parseSrcWidth: React.PropTypes.number,
parseSrcAllowedWidths: React.PropTypes.arrayOf(React.PropTypes.number),
Expand All @@ -61,14 +61,16 @@ Image.propTypes = {
};

Image.defaultProps = {
heightWidthRatio: 1 // Square
widthHeightRatio: 1, // Square
parseSrcDoubleForRetina: true
};

/**
* Check whether screen is high density (1.3 or 2dpi)
* From http://stackoverflow.com/a/20413768/56976
*/
function isHighDensity(){
if (typeof window === 'undefined') return false;
return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)').matches)) || (window.devicePixelRatio && window.devicePixelRatio > 1.3));
}

Expand Down
8 changes: 4 additions & 4 deletions examples/unsplash/src/components/Image/ImageBlock.js
@@ -1,12 +1,12 @@
import React from 'react';

const ImageBlock = ({ heightWidthRatio, children }) => {
const ImageBlock = ({ widthHeightRatio, children }) => {

const style = {
position: 'relative',
width: '100%',
overflow: 'hidden',
paddingBottom: heightWidthRatio * 100 + '%'
paddingBottom: 100 / widthHeightRatio + '%'
}

return (
Expand All @@ -17,11 +17,11 @@ const ImageBlock = ({ heightWidthRatio, children }) => {
}

ImageBlock.defaultProps = {
heightWidthRatio: 1 // Square
widthHeightRatio: 1
};

ImageBlock.propTypes = {
heightWidthRatio: React.PropTypes.number,
widthHeightRatio: React.PropTypes.number,
children: React.PropTypes.node
};

Expand Down
33 changes: 33 additions & 0 deletions examples/unsplash/src/components/Infinite/package.json
@@ -0,0 +1,33 @@
{
"name": "infinite",
"version": "0.0.1",
"description": "",
"main": "lib/infinite.js",
"scripts": {

},
"peerDependencies": {
"react": ">=0.12.0 <16.0.0"
},
"devDependencies": {
"babel-core": "6.18.2",
"babel-loader": "6.2.8",
"babel-preset-es2015": "6.18.0",
"babel-preset-react": "6.16.0",
"url-loader": "^0.5.7",
"webpack": "1.13.3"
},
"repository": {
"type": "git",
"url": ""
},
"author": "Gabe Ragland <gabe.ragland@gmail.com>",
"license": "MIT",
"bugs": {
"url": ""
},
"keywords": [

],
"homepage": ""
}
30 changes: 30 additions & 0 deletions examples/unsplash/src/components/Layout.js
@@ -0,0 +1,30 @@
import React from 'react';

const Layout = ({ markup, head, serverData }) => (
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />

{head.title.toComponent()}
{head.meta.toComponent()}

<link rel='stylesheet' type='text/css' href='/assets/style.css'/>
</head>
<body>
<div id='app' dangerouslySetInnerHTML={{__html: markup}}></div>

{ serverData &&
<script id='serverData' type='application/json' dangerouslySetInnerHTML={{__html: safeStringify(serverData)}}></script>
}

<script src='/assets/bundle.js'></script>
</body>
</html>
);

function safeStringify(obj){
return JSON.stringify(obj).replace(/<\/script/g, '<\\/script').replace(/<!--/g, '<\\!--');
}

export default Layout;
3 changes: 2 additions & 1 deletion examples/unsplash/src/components/Photo.js
Expand Up @@ -13,7 +13,8 @@ const Photo = ({ data, parentWidth }) => {
parseSrc={true}
parseSrcWidth={parentWidth}
parseSrcAllowedWidths={allowedSrcWidths}
parseSrcDoubleForRetina={true} />
parseSrcDoubleForRetina={true}
widthHeightRatio={1/1} />
<div style={{ position: 'absolute', top: 10, right: 10, backgroundColor: '#fff', color: '#000', padding: '0.3em 0.6em', opacity: '1' }}>
{parentWidth}px
</div>
Expand Down

0 comments on commit 6efc030

Please sign in to comment.