KEMBAR78
Building Universal Web Apps with React ForwardJS 2017 | PDF
Building Universal Web
Apps with React
Elyse Kolker Gordon
Twitter: @sfdrummerjs, Github: @elyseko
Web Lead, Vevo
Code: bit.ly/universal-app-react
Get it 40% off!
bit.ly/isomorphicdevjs
Code: ctwforwjs17
Win a free ebook:
http://bit.ly/2m0pCiP
Universal or
Isomorphic?
Write code once, run it in two environments (node & browser).
Overview of Universal/Isomorphic Web Apps
Why would you want to build an universal web app
Universal App Architecture
Challenges and Tradeoffs
Topics:
Overview: What is a
Universal App?
Server-side rendered applications
Overview
Server-side rendered applications
Overview
Server-side rendered applications
Overview
Server-side rendered applications
Overview
Server-side rendered applications
Overview
Server-side rendered applications
Overview
Single Page Application (SPA)
Overview
Single Page Application (SPA)
Overview
Single Page Application (SPA)
Overview
Single Page Application (SPA)
Overview
Single Page Application (SPA)
Overview
Why not both?
Overview
Overview
Overview
Overview
Overview
Overview
Overview
Overview
Overview
Why build it?
#1 SEO
Getting your website (or cat video) to
rank the highest can make or break
your success as a business.
Why build it?
#2 Perceived
Performance
Users should think the site is fast. This
means showing content (not loading
spinners) as soon as possible.
Why build it?
Everything is JavaScript! Most of your
code will run on both the server and
the browser.
#3 Maintenance and Developer
benefits
Why build it?
Universal App
Architecture
In this section
Technologies required to build an
universal app
Loading a single page of the app in
the isomorphic flow
Architecture
Code: bit.ly/universal-app-react
In this section
Technologies required to build an
universal app
Loading a single page of the app in
the isomorphic flow
Architecture
Code: bit.ly/universal-app-react
In this section
Technologies required to build an
universal app
Loading a single page of the app in
the isomorphic flow
Architecture
Code: bit.ly/universal-app-react
Architecture
Architecture
Node + Express
Architecture
Webpack
Architecture
Why webpack?
NPM packages
ES6/ES7 with babel
CSS in your browser bundle
Advanced features
Architecture: webpack
Architecture: webpack
Architecture: webpack
Code
Architecture: webpack
Code Parser
Architecture: webpack
Code Parser Transforms
Architecture: webpack
Code Parser Transforms
Compiled
App Code
Architecture: wepack
"scripts": {

"build:browser": "webpack",

"prestart": "npm run build:browser",

"start": "node src/server.js"

}
package.json
module.exports = {

entry: "./src/main.jsx",

devtool: "source-map",

output: {

path: __dirname + '/
src/',

filename: "browser.js"

},
Architecture: webpack
webpack.config.js
module.exports = {

...
Architecture: webpack
module: {

loaders: [

{

test: /.(jsx|es6)$/,

exclude: /node_modules|examples/,

loader: "babel-loader"

},

{

test: /.css$/,

loaders: ['style', 'css']

}

]
webpack.config.js
React
Architecture
Virtual DOM
Architecture: React
Virtual DOM
Architecture: React
Virtual DOM
Architecture: React
Virtual DOM
Architecture: React
Architecture: React
import ReactDOM from 'react-dom';

ReactDOM.render(
<RootComponent {...props} />,
document.getElementById(‘react-
content’)
);
Architecture: React
import { renderToString } from 'react-dom/
server';

renderToString(<RootComponent {...props} />);
React Router
Architecture
Architecture: React Router
Architecture: React Router
ReactDOM.render(

<Router routes={sharedRoutes(store)}
history={browserHistory} />,

document.getElementById('react-content')

);
Architecture: React RouterrenderView.jsx
import React from 'react';

import { match, RouterContext } from 'react-router';

import { routes } from '../shared/sharedRoutes';



export default function renderView(req, res, next) {

}

Architecture: React RouterrenderView.jsx
import React from 'react';

import { match, RouterContext } from 'react-router';

import { routes } from '../shared/sharedRoutes';



export default function renderView(req, res, next) {

}

Architecture: React Router
match(matchOpts, handleMatchResult);
renderView.jsx
import React from 'react';

import { match, RouterContext } from 'react-router';

import { routes } from '../shared/sharedRoutes';



export default function renderView(req, res, next) {

const matchOpts = {

routes: routes(),

location: req.url

};





}

Architecture: React Router
match(matchOpts, handleMatchResult);
renderView.jsx
match(matchOpts, handleMatchResult);
import React from 'react';

import { match, RouterContext } from 'react-router';

import { routes } from '../shared/sharedRoutes';



export default function renderView(req, res, next) {

const matchOpts = {

routes: routes(),

location: req.url

};





}

Architecture: React Router
const handleMatchResult = (error, redirectLocation, renderProps) =>
{

if (!error && !redirectLocation && renderProps) {
// render code
}
}
match(matchOpts, handleMatchResult);
renderView.jsx
match(matchOpts, handleMatchResult);
const matchOpts = {

routes: routes(),

location: req.url

};

Redux
Architecture
Architecture: Redux
View
Architecture: Redux
View
Actions
Architecture: Redux
View
Actions
Architecture: Redux
Reducers
View
Actions
Store
Architecture: Redux
Reducers
View
Actions
Store
Architecture: Redux
Reducers
Build the cart
Architecture
Architecture: Create the Cart
Architecture: Create the Cart
sharedRoutes.jsx
<Route path="/" component={App}>

<Route path="/cart" component={Cart} />

<Route path="/cart/payment" component={Payment} />

<Route path="/products" component={Products} />

<Route path="/profile" component={Profile} />

<Route path="/login" component={Login} />

</Route>
Architecture: Create the Cart
sharedRoutes.jsx
Architecture: Create the Cart
export const routes = () => {
}
<Route path="/" component={App}>

<Route path="/cart" component={Cart} />

<Route path="/cart/payment" component={Payment} />

<Route path="/products" component={Products} />

<Route path="/profile" component={Profile} />

<Route path="/login" component={Login} />

</Route>
sharedRoutes.jsx
Architecture: Create the Cart
Architecture: Create the Cart
Architecture: Create the Cart
Architecture: Create the Cart
item.jsx
const Item = (props) => {

};

Architecture: Create the Cart
item.jsx
return (

<div className="item">

<div className="ui tiny image">

<img src={props.thumbnail} alt="cart" />

</div>

<div className="middle aligned content">

{props.name}

</div>

<div className="right aligned content">

${props.price}

</div>

</div>

);

const Item = (props) => {

};

Architecture: Create the Cart
item.jsx
return (

<div className="item">

<div className="ui tiny image">

<img src={props.thumbnail} alt="cart" />

</div>

<div className="middle aligned content">

{props.name}

</div>

<div className="right aligned content">

${props.price}

</div>

</div>

);

const Item = (props) => {

};

Architecture: Create the Cart
item.jsx
return (

<div className="item">

<div className="ui tiny image">

<img src={props.thumbnail} alt="cart" />

</div>

<div className="middle aligned content">

{props.name}

</div>

<div className="right aligned content">

${props.price}

</div>

</div>

);

return (

<div className="item">

<div className="ui tiny image">

<img src={props.thumbnail} alt="cart" />

</div>

<div className="middle aligned content">

{props.name}

</div>

<div className="right aligned content">

${props.price}

</div>

</div>

);

const Item = (props) => {

};

const Item = (props) => {

};

Architecture: Create the Cart
item.jsx
Architecture: Create the Cartcart.jsx
render() {

return (

<div className="cart main ui segment">

<div className="ui segment divided items">

{this.renderItems()}

</div>
</div>

);

}
Architecture: Create the Cartcart.jsx
render() {

return (

<div className="cart main ui segment">

<div className="ui segment divided items">

{this.renderItems()}

</div>
</div>

);

}
Architecture: Create the Cart
<div className="ui right rail">

<div className="ui segment">

<span>Total: </span><span>${this.getTotal()}</span>

<button onClick={this.proceedToCheckout} className="ui positive basic button">

Checkout

</button>

</div>
</div>
render() {

return (

<div className="cart main ui segment">

<div className="ui segment divided items">

{this.renderItems()}

</div>
</div>

);

}
cart.jsx


import cartActions from '../shared/cart-action-creators.es6';



export class CartComponent extends Component {



static loadData() {

return [

cartActions.getCartItems

];

}

render() {}

}

Architecture: Create the Cart
cart.jsx




export class CartComponent extends Component {}


function mapStateToProps(state) {}



function mapDispatchToProps(dispatch) {}



export default connect(mapStateToProps, mapDispatchToProps)
(CartComponent);
Architecture: Create the Cart
cart.jsx
import fetch from 'isomorphic-fetch';



export const GET_CART_ITEMS = 'GET_CART_ITEMS';



export function getCartItems() {
Architecture: Create the Cart
cart-action-creators.es6
import fetch from 'isomorphic-fetch';



export const GET_CART_ITEMS = 'GET_CART_ITEMS';



export function getCartItems() {

return (dispatch) => {

return fetch('http://localhost:3000/api/user/cart', {

method: 'GET'

})
Architecture: Create the Cart
cart-action-creators.es6
import fetch from 'isomorphic-fetch';



export const GET_CART_ITEMS = 'GET_CART_ITEMS';



export function getCartItems() {

return (dispatch) => {

return fetch('http://localhost:3000/api/user/cart', {

method: 'GET'

})
Architecture: Create the Cart
cart-action-creators.es6
.then((response) => {

return response.json().then((data) => {

return dispatch({

type: GET_CART_ITEMS,

data: data.items

});

});

})
Architecture: Create the Cart
import { GET_CART_ITEMS } from './cart-action-creators.es6';



export default function cart(state = {}, action) {

switch (action.type) {

case GET_CART_ITEMS:

return {

...state,

items: action.data

};

}

}
cart-reducer.es6
Architecture: Create the Cart
import { GET_CART_ITEMS } from './cart-action-creators.es6';



export default function cart(state = {}, action) {

switch (action.type) {

case GET_CART_ITEMS:

return {

...state,

items: action.data

};

}

}
import { GET_CART_ITEMS } from './cart-action-creators.es6';



export default function cart(state = {}, action) {

switch (action.type) {

case GET_CART_ITEMS:

return {

...state,

items: action.data

};

}

}
cart-reducer.es6
Server setup
Architecture
HTML output from server
Architecture: Server setup
Architecture: Server setup
export default function renderView(req, res, next) {

const matchOpts = {};

const handleMatchResult = (error, redirectLocation, renderProps) => {

if (!error && !redirectLocation && renderProps) {

const store = initRedux();

let actions = renderProps.components.map((component) => {

// return actions from loadData() on each component

});
renderView.jsx
Architecture: Server setup


Promise.all(promises).then(() => {

const serverState = store.getState();



renderView.jsx
Architecture: Server setup


Promise.all(promises).then(() => {

const serverState = store.getState();



const stringifiedServerState = JSON.stringify(serverState);


Promise.all(promises).then(() => {

const serverState = store.getState();



renderView.jsx
Architecture: Server setup


Promise.all(promises).then(() => {

const serverState = store.getState();



const stringifiedServerState = JSON.stringify(serverState);


Promise.all(promises).then(() => {

const serverState = store.getState();





const app = renderToString(

<Provider store={store}>

<RouterContext routes={routes} {...renderProps} />

</Provider>

);
const stringifiedServerState = JSON.stringify(serverState);
renderView.jsx
Architecture: Server setup


Promise.all(promises).then(() => {

const serverState = store.getState();



const stringifiedServerState = JSON.stringify(serverState);


Promise.all(promises).then(() => {

const serverState = store.getState();





const app = renderToString(

<Provider store={store}>

<RouterContext routes={routes} {...renderProps} />

</Provider>

);
const stringifiedServerState = JSON.stringify(serverState);


const html = renderToString(

<HTML html={app} serverState={stringifiedServerState} />

);





const app = renderToString(

<Provider store={store}>

<RouterContext routes={routes} {...renderProps} />

</Provider>

);
renderView.jsx
Architecture: Server setup


Promise.all(promises).then(() => {

const serverState = store.getState();



const stringifiedServerState = JSON.stringify(serverState);


Promise.all(promises).then(() => {

const serverState = store.getState();





const app = renderToString(

<Provider store={store}>

<RouterContext routes={routes} {...renderProps} />

</Provider>

);
const stringifiedServerState = JSON.stringify(serverState);


const html = renderToString(

<HTML html={app} serverState={stringifiedServerState} />

);



return res.send(`<!DOCTYPE html>${html}`);


const app = renderToString(

<Provider store={store}>

<RouterContext routes={routes} {...renderProps} />

</Provider>

);

const html = renderToString(

<HTML html={app} serverState={stringifiedServerState} />

);



renderView.jsx
Architecture: Server setup
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
html.jsx
Architecture: Server setup
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
html.jsx
Architecture: Server setup
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
html.jsx
Architecture: Server setup
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
const HTML = (props) => {

return (

<html lang="en">

<head></head>

<body>

<div

id="react-content"

dangerouslySetInnerHTML={{ __html: props.html }}/>

<script dangerouslySetInnerHTML={{

__html: `

window.__SERIALIZED_STATE__ =

JSON.stringify(${props.serverState})

`

}}/>

<script type="application/javascript" src="/browser.js" />

</body>

</html>

);

};
html.jsx
Clicking the button results doesn’t do anything
Architecture: Server Setup
App State available in the browser.
Architecture: Server setup
Browser setup
Architecture


const initialState = JSON.parse(window.__SERIALIZED_STATE__);

const store = initRedux(initialState);

Architecture: Browser setup


const initialState = JSON.parse(window.__SERIALIZED_STATE__);

const store = initRedux(initialState);

Architecture: Browser setup


function init() {

ReactDOM.render(

<Provider store={store}>

<Router routes={sharedRoutes(store)} history={browserHistory}
/>

</Provider>, document.getElementById('react-content'));

}



init();


const initialState = JSON.parse(window.__SERIALIZED_STATE__);

const store = initRedux(initialState);

Architecture: Browser setup
Challenges and
Tradeoffs
Competing envs
Challenges and Tradeoffs
Challenges and Tradeoffs
Challenges and Tradeoffs
if (process.env.BROWSER) {

// browser only code

}
Performance
Challenges and Tradeoffs
Server Performance Strategies
Only render above the fold content on the server (SEO tradeoff)
Use streams
Caching
Challenges and Tradeoffs
Caching
Challenges and Tradeoffs
Challenges and Tradeoffs
Challenges and Tradeoffs
Challenges and Tradeoffs
Two Options
In memory caching
CDN/Edge caching
Walmart Labs Server Side Render Cache (Electrode)
Challenges and Tradeoffs
Complexity
Challenges and Tradeoffs
Building Universal Apps requires you to think about your
application flow in a new way.

Building Universal Web Apps with React ForwardJS 2017

  • 1.
    Building Universal Web Appswith React Elyse Kolker Gordon Twitter: @sfdrummerjs, Github: @elyseko Web Lead, Vevo Code: bit.ly/universal-app-react
  • 2.
    Get it 40%off! bit.ly/isomorphicdevjs Code: ctwforwjs17 Win a free ebook: http://bit.ly/2m0pCiP
  • 3.
  • 4.
    Write code once,run it in two environments (node & browser).
  • 5.
    Overview of Universal/IsomorphicWeb Apps Why would you want to build an universal web app Universal App Architecture Challenges and Tradeoffs Topics:
  • 6.
    Overview: What isa Universal App?
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
    #1 SEO Getting yourwebsite (or cat video) to rank the highest can make or break your success as a business. Why build it?
  • 29.
    #2 Perceived Performance Users shouldthink the site is fast. This means showing content (not loading spinners) as soon as possible. Why build it?
  • 30.
    Everything is JavaScript!Most of your code will run on both the server and the browser. #3 Maintenance and Developer benefits Why build it?
  • 31.
  • 32.
    In this section Technologiesrequired to build an universal app Loading a single page of the app in the isomorphic flow Architecture Code: bit.ly/universal-app-react
  • 33.
    In this section Technologiesrequired to build an universal app Loading a single page of the app in the isomorphic flow Architecture Code: bit.ly/universal-app-react
  • 34.
    In this section Technologiesrequired to build an universal app Loading a single page of the app in the isomorphic flow Architecture Code: bit.ly/universal-app-react
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
    Why webpack? NPM packages ES6/ES7with babel CSS in your browser bundle Advanced features Architecture: webpack
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
    Architecture: webpack Code ParserTransforms Compiled App Code
  • 45.
    Architecture: wepack "scripts": {
 "build:browser":"webpack",
 "prestart": "npm run build:browser",
 "start": "node src/server.js"
 } package.json
  • 46.
    module.exports = {
 entry:"./src/main.jsx",
 devtool: "source-map",
 output: {
 path: __dirname + '/ src/',
 filename: "browser.js"
 }, Architecture: webpack webpack.config.js
  • 47.
    module.exports = {
 ... Architecture:webpack module: {
 loaders: [
 {
 test: /.(jsx|es6)$/,
 exclude: /node_modules|examples/,
 loader: "babel-loader"
 },
 {
 test: /.css$/,
 loaders: ['style', 'css']
 }
 ] webpack.config.js
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
    Architecture: React import ReactDOMfrom 'react-dom';
 ReactDOM.render( <RootComponent {...props} />, document.getElementById(‘react- content’) );
  • 54.
    Architecture: React import {renderToString } from 'react-dom/ server';
 renderToString(<RootComponent {...props} />);
  • 55.
  • 56.
  • 57.
    Architecture: React Router ReactDOM.render(
 <Routerroutes={sharedRoutes(store)} history={browserHistory} />,
 document.getElementById('react-content')
 );
  • 58.
  • 59.
    import React from'react';
 import { match, RouterContext } from 'react-router';
 import { routes } from '../shared/sharedRoutes';
 
 export default function renderView(req, res, next) {
 }
 Architecture: React RouterrenderView.jsx
  • 60.
    import React from'react';
 import { match, RouterContext } from 'react-router';
 import { routes } from '../shared/sharedRoutes';
 
 export default function renderView(req, res, next) {
 }
 Architecture: React Router match(matchOpts, handleMatchResult); renderView.jsx
  • 61.
    import React from'react';
 import { match, RouterContext } from 'react-router';
 import { routes } from '../shared/sharedRoutes';
 
 export default function renderView(req, res, next) {
 const matchOpts = {
 routes: routes(),
 location: req.url
 };
 
 
 }
 Architecture: React Router match(matchOpts, handleMatchResult); renderView.jsx match(matchOpts, handleMatchResult);
  • 62.
    import React from'react';
 import { match, RouterContext } from 'react-router';
 import { routes } from '../shared/sharedRoutes';
 
 export default function renderView(req, res, next) {
 const matchOpts = {
 routes: routes(),
 location: req.url
 };
 
 
 }
 Architecture: React Router const handleMatchResult = (error, redirectLocation, renderProps) => {
 if (!error && !redirectLocation && renderProps) { // render code } } match(matchOpts, handleMatchResult); renderView.jsx match(matchOpts, handleMatchResult); const matchOpts = {
 routes: routes(),
 location: req.url
 };

  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
    Architecture: Create theCart sharedRoutes.jsx
  • 73.
    <Route path="/" component={App}>
 <Routepath="/cart" component={Cart} />
 <Route path="/cart/payment" component={Payment} />
 <Route path="/products" component={Products} />
 <Route path="/profile" component={Profile} />
 <Route path="/login" component={Login} />
 </Route> Architecture: Create the Cart sharedRoutes.jsx
  • 74.
    Architecture: Create theCart export const routes = () => { } <Route path="/" component={App}>
 <Route path="/cart" component={Cart} />
 <Route path="/cart/payment" component={Payment} />
 <Route path="/products" component={Products} />
 <Route path="/profile" component={Profile} />
 <Route path="/login" component={Login} />
 </Route> sharedRoutes.jsx
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
    const Item =(props) => {
 };
 Architecture: Create the Cart item.jsx
  • 80.
    return (
 <div className="item">
 <divclassName="ui tiny image">
 <img src={props.thumbnail} alt="cart" />
 </div>
 <div className="middle aligned content">
 {props.name}
 </div>
 <div className="right aligned content">
 ${props.price}
 </div>
 </div>
 );
 const Item = (props) => {
 };
 Architecture: Create the Cart item.jsx
  • 81.
    return (
 <div className="item">
 <divclassName="ui tiny image">
 <img src={props.thumbnail} alt="cart" />
 </div>
 <div className="middle aligned content">
 {props.name}
 </div>
 <div className="right aligned content">
 ${props.price}
 </div>
 </div>
 );
 const Item = (props) => {
 };
 Architecture: Create the Cart item.jsx
  • 82.
    return (
 <div className="item">
 <divclassName="ui tiny image">
 <img src={props.thumbnail} alt="cart" />
 </div>
 <div className="middle aligned content">
 {props.name}
 </div>
 <div className="right aligned content">
 ${props.price}
 </div>
 </div>
 );
 return (
 <div className="item">
 <div className="ui tiny image">
 <img src={props.thumbnail} alt="cart" />
 </div>
 <div className="middle aligned content">
 {props.name}
 </div>
 <div className="right aligned content">
 ${props.price}
 </div>
 </div>
 );
 const Item = (props) => {
 };
 const Item = (props) => {
 };
 Architecture: Create the Cart item.jsx
  • 83.
  • 84.
    render() {
 return (
 <divclassName="cart main ui segment">
 <div className="ui segment divided items">
 {this.renderItems()}
 </div> </div>
 );
 } Architecture: Create the Cartcart.jsx
  • 85.
    render() {
 return (
 <divclassName="cart main ui segment">
 <div className="ui segment divided items">
 {this.renderItems()}
 </div> </div>
 );
 } Architecture: Create the Cart <div className="ui right rail">
 <div className="ui segment">
 <span>Total: </span><span>${this.getTotal()}</span>
 <button onClick={this.proceedToCheckout} className="ui positive basic button">
 Checkout
 </button>
 </div> </div> render() {
 return (
 <div className="cart main ui segment">
 <div className="ui segment divided items">
 {this.renderItems()}
 </div> </div>
 );
 } cart.jsx
  • 86.
    
 import cartActions from'../shared/cart-action-creators.es6';
 
 export class CartComponent extends Component {
 
 static loadData() {
 return [
 cartActions.getCartItems
 ];
 }
 render() {}
 }
 Architecture: Create the Cart cart.jsx
  • 87.
    
 
 export class CartComponentextends Component {} 
 function mapStateToProps(state) {}
 
 function mapDispatchToProps(dispatch) {}
 
 export default connect(mapStateToProps, mapDispatchToProps) (CartComponent); Architecture: Create the Cart cart.jsx
  • 88.
    import fetch from'isomorphic-fetch';
 
 export const GET_CART_ITEMS = 'GET_CART_ITEMS';
 
 export function getCartItems() { Architecture: Create the Cart cart-action-creators.es6
  • 89.
    import fetch from'isomorphic-fetch';
 
 export const GET_CART_ITEMS = 'GET_CART_ITEMS';
 
 export function getCartItems() {
 return (dispatch) => {
 return fetch('http://localhost:3000/api/user/cart', {
 method: 'GET'
 }) Architecture: Create the Cart cart-action-creators.es6
  • 90.
    import fetch from'isomorphic-fetch';
 
 export const GET_CART_ITEMS = 'GET_CART_ITEMS';
 
 export function getCartItems() {
 return (dispatch) => {
 return fetch('http://localhost:3000/api/user/cart', {
 method: 'GET'
 }) Architecture: Create the Cart cart-action-creators.es6 .then((response) => {
 return response.json().then((data) => {
 return dispatch({
 type: GET_CART_ITEMS,
 data: data.items
 });
 });
 })
  • 91.
    Architecture: Create theCart import { GET_CART_ITEMS } from './cart-action-creators.es6';
 
 export default function cart(state = {}, action) {
 switch (action.type) {
 case GET_CART_ITEMS:
 return {
 ...state,
 items: action.data
 };
 }
 } cart-reducer.es6
  • 92.
    Architecture: Create theCart import { GET_CART_ITEMS } from './cart-action-creators.es6';
 
 export default function cart(state = {}, action) {
 switch (action.type) {
 case GET_CART_ITEMS:
 return {
 ...state,
 items: action.data
 };
 }
 } import { GET_CART_ITEMS } from './cart-action-creators.es6';
 
 export default function cart(state = {}, action) {
 switch (action.type) {
 case GET_CART_ITEMS:
 return {
 ...state,
 items: action.data
 };
 }
 } cart-reducer.es6
  • 93.
  • 94.
    HTML output fromserver Architecture: Server setup
  • 95.
    Architecture: Server setup exportdefault function renderView(req, res, next) {
 const matchOpts = {};
 const handleMatchResult = (error, redirectLocation, renderProps) => {
 if (!error && !redirectLocation && renderProps) {
 const store = initRedux();
 let actions = renderProps.components.map((component) => {
 // return actions from loadData() on each component
 }); renderView.jsx
  • 96.
    Architecture: Server setup 
 Promise.all(promises).then(()=> {
 const serverState = store.getState();
 
 renderView.jsx
  • 97.
    Architecture: Server setup 
 Promise.all(promises).then(()=> {
 const serverState = store.getState();
 
 const stringifiedServerState = JSON.stringify(serverState); 
 Promise.all(promises).then(() => {
 const serverState = store.getState();
 
 renderView.jsx
  • 98.
    Architecture: Server setup 
 Promise.all(promises).then(()=> {
 const serverState = store.getState();
 
 const stringifiedServerState = JSON.stringify(serverState); 
 Promise.all(promises).then(() => {
 const serverState = store.getState();
 
 
 const app = renderToString(
 <Provider store={store}>
 <RouterContext routes={routes} {...renderProps} />
 </Provider>
 ); const stringifiedServerState = JSON.stringify(serverState); renderView.jsx
  • 99.
    Architecture: Server setup 
 Promise.all(promises).then(()=> {
 const serverState = store.getState();
 
 const stringifiedServerState = JSON.stringify(serverState); 
 Promise.all(promises).then(() => {
 const serverState = store.getState();
 
 
 const app = renderToString(
 <Provider store={store}>
 <RouterContext routes={routes} {...renderProps} />
 </Provider>
 ); const stringifiedServerState = JSON.stringify(serverState); 
 const html = renderToString(
 <HTML html={app} serverState={stringifiedServerState} />
 );
 
 
 const app = renderToString(
 <Provider store={store}>
 <RouterContext routes={routes} {...renderProps} />
 </Provider>
 ); renderView.jsx
  • 100.
    Architecture: Server setup 
 Promise.all(promises).then(()=> {
 const serverState = store.getState();
 
 const stringifiedServerState = JSON.stringify(serverState); 
 Promise.all(promises).then(() => {
 const serverState = store.getState();
 
 
 const app = renderToString(
 <Provider store={store}>
 <RouterContext routes={routes} {...renderProps} />
 </Provider>
 ); const stringifiedServerState = JSON.stringify(serverState); 
 const html = renderToString(
 <HTML html={app} serverState={stringifiedServerState} />
 );
 
 return res.send(`<!DOCTYPE html>${html}`); 
 const app = renderToString(
 <Provider store={store}>
 <RouterContext routes={routes} {...renderProps} />
 </Provider>
 );
 const html = renderToString(
 <HTML html={app} serverState={stringifiedServerState} />
 );
 
 renderView.jsx
  • 101.
    Architecture: Server setup constHTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; html.jsx
  • 102.
    Architecture: Server setup constHTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; html.jsx
  • 103.
    Architecture: Server setup constHTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; html.jsx
  • 104.
    Architecture: Server setup constHTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; const HTML = (props) => {
 return (
 <html lang="en">
 <head></head>
 <body>
 <div
 id="react-content"
 dangerouslySetInnerHTML={{ __html: props.html }}/>
 <script dangerouslySetInnerHTML={{
 __html: `
 window.__SERIALIZED_STATE__ =
 JSON.stringify(${props.serverState})
 `
 }}/>
 <script type="application/javascript" src="/browser.js" />
 </body>
 </html>
 );
 }; html.jsx
  • 105.
    Clicking the buttonresults doesn’t do anything Architecture: Server Setup
  • 106.
    App State availablein the browser. Architecture: Server setup
  • 107.
  • 108.
    
 const initialState =JSON.parse(window.__SERIALIZED_STATE__);
 const store = initRedux(initialState);
 Architecture: Browser setup
  • 109.
    
 const initialState =JSON.parse(window.__SERIALIZED_STATE__);
 const store = initRedux(initialState);
 Architecture: Browser setup 
 function init() {
 ReactDOM.render(
 <Provider store={store}>
 <Router routes={sharedRoutes(store)} history={browserHistory} />
 </Provider>, document.getElementById('react-content'));
 }
 
 init(); 
 const initialState = JSON.parse(window.__SERIALIZED_STATE__);
 const store = initRedux(initialState);

  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
    Challenges and Tradeoffs if(process.env.BROWSER) {
 // browser only code
 }
  • 115.
  • 116.
    Server Performance Strategies Onlyrender above the fold content on the server (SEO tradeoff) Use streams Caching Challenges and Tradeoffs
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
    Two Options In memorycaching CDN/Edge caching Walmart Labs Server Side Render Cache (Electrode) Challenges and Tradeoffs
  • 122.
  • 123.
    Building Universal Appsrequires you to think about your application flow in a new way.