SlideShare a Scribd company logo
Universal JS Web Applications
with React
Luciano Mammino (@loige)
Photo by Ryan Holloway on Unsplash
loige.link/uni-js-workshop
ROVINJ, Aug 30 2017
Who is Luciano?
Principal Application Engineer
Connect
Website - Twitter - GitHub - Linkedin
fullstackbulletin.com
-15% Print (NpBK15WSCR)
-20% eBook (NeBK20WSCR)
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
13:50
ISOMORPHIC
UNIVERSAL… what?
loige.link/universal-js-story
NOT ONLY
FOR THE WEB...
Desktop applications
Mobile applications
Hardware!
ADVANTAGES
OF UNIVERSAL JAVASCRIPT
"JavaScript-only" development
Maintainability
Better SEO
Faster "perceived" load time
MOAR
ADVANTAGES...
Keep using React/JS paradigms for "static" websites
Speed up content loading with linkprefetch
loige.link/universal-react-made-easy-talk
In the wild
Seems cool… but...
MODULE SHARING
Use Node.js modules in the browser
UNIVERSAL RENDERING
Render the views of the application from the server
(first request) and then in the browser (next requests)
UNIVERSAL ROUTING
Recognise the view associated to the current route
from both the server and the browser.
UNIVERSAL DATA RETRIEVAL
Access data (and APIs)
from both the server and the browser.
AXIOS UNIVERSAL
FETCH
UNIVERSAL STATE
Manage changes on the state tree
both on the server and the client...
ALTERNATIVE JS?!
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
OK… let's stop complaining and start coding!
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
14:00
WHAT ARE WE GOING TO BUILD?
loige.link/judo-heroes-app​
loige.link/judo-heroes-tutorial
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
Quick Demo
WHAT TOOLS ARE WE GOING TO USE?
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
14:05
A React primer
● Components
● JSX
● Composition
● Props
● Events & State
Everything is a component
● The view is made up by a tree of components
(like the DOM is made up by a tree of HTML tags)
● Components can be created in different ways (pure functions, class syntax)
● A component can include other components as children
● Components content is defined in JSX
● Components can receive data from the outside (props)
● Components can manage internal data (state)
● Components can be installed (rendered) in an HTML page
● Generally you'll have one main component containing all the others in your page
Online React Sandbox
codesandbox.io
Pure function syntax
import React from 'react';
import { render } from 'react-dom';
const IAmAComponent = () => (
<div>
<h1>This is a component as a pure function</h1>
</div>
);
render(<IAmAComponent />,
document.getElementById('root'));
Class extend syntax
import React from 'react';
import { render } from 'react-dom';
class IAmAnotherComponent extends React.Component {
render() {
return (
<div>
<h1>This is a component created with
ES6 classes</h1>
</div>
)
}
}
render(<IAmAnotherComponent />,
document.getElementById('root'));
✏ Exercise - HelloWorld component
Create an HelloWorld
component
that prints "Hello World"
Render the HelloWorld
component on the page
https://codesandbox.io/s/p34lnpm6q0
14:20
JSX
● It looks like HTML/XML, considered a superset of JavaScript
● It can contain JavaScript expressions
● It is "transpiled" to plain JavaScript using Babel
● Can include regular HTML tags (they start with a lowercase character)
● … or other components (they start with an uppercase character)
● Can include regular HTML attributes
… With some exceptions:
○ class becomes className
○ properties with hyphens becomes camelcase
(background-color -> backgroundColor)
<div className="shopping-list">
<ul>
<li>Mushrooms</li>
<li>Lasagna</li>
</ul>
</div>
React.createElement(
"div",
{ className: "shopping-list" },
React.createElement(
"ul",
null,
React.createElement(
"li",
null,
"Mushrooms"
),
React.createElement(
"li",
null,
"Lasagna"
)
)
);
JSX Compiled JS (By Babel/React Transpiler)
JSX examples
import React from 'react';
import { render } from 'react-dom';
const SampleComponent = () => (
<h1>
Last render time:
<strong style={{backgroundColor: 'yellow', padding:
'2px'}}>
{(new Date).toISOString()}
</strong>
</h1>
)
render(<SampleComponent />,
document.getElementById('root'));
React components tree
ParentComponent
FirstChildComponent SecondChildComponent
Components Tree
import React from 'react';
import { render } from 'react-dom';
const FirstChildComponent = () => (
<div style={{ background: 'peru', padding: '2px' }}>
<h2>FirstChildComponent</h2>
</div>
)
const SecondChildComponent = () => (
<div style={{ background: 'aqua', padding: '2px' }}>
<h2>SecondChildComponent</h2>
</div>
)
const ParentComponent = () => (
<div style={{ border: '2px dotted black', padding:
'4px'}}>
<h1>ParentComponent</h1>
<FirstChildComponent/>
<SecondChildComponent/>
</div>
)
render(<ParentComponent />,
document.getElementById('root'));
✏ Exercise - Combine components
Create a react app with 3 components
● Title component
● Content component
● Footer component
Then create a component called Layout that
contains all the 3 as shown aside
Then render the Layout component on the page
https://codesandbox.io/s/48ok2yv3z7
14:35
Props
● Attributes set to components are called Props
● They are used to pass "input" data into components (from the parent component)
● They allow to create "reusable" components
import React from 'react';
import { render } from 'react-dom';
const PrintProps = (props) => (
<div>
<h1>Received props</h1>
<pre>{
JSON.stringify(props, null, 2)
}</pre>
</div>
)
render(<PrintProps
foo="bar" bar="baz" qoo="qoo" />,
document.getElementById('root'));
Reusable components
import React from 'react';
import { render } from 'react-dom';
const ChildComponent = (props) => (
<div
style={{ background: props.color, padding: '2px' }}>
<h2>{props.name}</h2>
</div>
)
const ParentComponent = () => (
<div
style={{ border: '2px dotted black', padding: '4px'}}>
<h1>ParentComponent</h1>
<ChildComponent
color="peru" name="FirstChildComponent"/>
<ChildComponent
color="aqua" name="SecondChildComponent"/>
</div>
)
render(<ParentComponent />,
document.getElementById('root'));
✏ Exercise - HelloWorld component with props
Create an HelloWorld component that prints
"Hello NAME", where NAME is coming from a
props called name
Render the HelloWorld component on the page
passing your name as prop
https://codesandbox.io/s/5vmr4o2m2n
14:45
Decorator components
import React from 'react';
import { render } from 'react-dom';
const Center = (props) => (
<div style={{
margin: 'auto',
width: '50%',
border: '2px solid #ccc',
textAlign: 'center'
}}>
{props.children}
</div>
)
const HelloWorld = ({name = 'World'}) => (<h1>Hello
{name}</h1>)
const App = () => (
<Center>
<HelloWorld name="Rovinj"/>
</Center>
)
render(<App />, document.getElementById('root'));
Iterations
import React from 'react';
import { render } from 'react-dom';
const difficoultThings = [
'Naming things',
'Cache invalidation',
'Off by one errors'
];
const App = () => (
<div>
<h2>The 2 most difficoult things in IT</h2>
<ol>
{difficoultThings.map((thing) => (
<li>{thing}</li>
) )}
</ol>
</div>
);
render(<App />, document.getElementById('root'));
Conditional rendering
import React from 'react';
import { render } from 'react-dom';
const money = 220;
const latestTransactions = [
{ description: 'Restaurant', amount: 50 },
{ description: 'Coffee', amount: 2 }
]
const Transactions = ({data}) => {
if (!data.length) {
return (<div>No transaction available</div>)
}
return (
<div>
<h3>Latest transactions</h3>
<ul>
{ data.map((transaction) => (
<li>{transaction.amount} ({transaction.description})</li>
)) }
</ul>
</div>
)
}
const App = () => (
<div>
<h2>You have { money > 0 ? 'some': 'no' } Money!</h2>
<p>Current Balance: {money}</p>
<Transactions data={latestTransactions}/>
</div>
);
render(<App />, document.getElementById('root'));
✏ Exercise - Your favourite CSS colors
Create a list of your favourite CSS colors.
Hint: create a component to visualize a single color
and render it multiple times based on the data
contained in an array of colors.
Bonus: Do something special with the color
"RebeccaPurple".
https://codesandbox.io/s/qqqz0n5z19
14:50
Events & state
import React from 'react';
import { render } from 'react-dom';
const rnd = () => Math.round(Math.random() * 255)
class RandomColor extends React.Component {
constructor(props) {
super(props);
this.state = {
color: props.startColor || 'red'
};
}
changeColor() {
const color = `rgb(${rnd()}, ${rnd()}, ${rnd()})`
this.setState({color})
}
render() {
return (
<div
onClick={this.changeColor.bind(this)}
style={{
padding: '80px',
textAlign: 'center',
background: this.state.color
}}
>Click me!</div>
)
}
}
render(<RandomColor startColor="RebeccaPurple" />,
document.getElementById('root'));
Quick Recap
● Everything is a component
● Composition over inheritance
● Pass application data through props ("data flows down")
● Components internal data constitutes state
● Components can react to events
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
15:00
Setup local dev environment
mkdir judo-heroes-2
cd judo-heroes-2
npm init -y
Install dependencies
npm i --save babel-cli@6.18.0 
babel-core@6.18.2 
babel-loader@7.1.2 
babel-preset-es2015@6.18.0 
babel-preset-react@6.16.0 
ejs@2.5.2 
express@5.0.0-alpha.5 
react@15.4.2 
react-dom@15.4.2 
react-router-dom@4.0.0 
webpack@2.7.0 
webpack-dev-server@2.7.1
Folders structure
mkdir -p 
src/components 
src/data 
src/views 
static
<- React components
<- Data file
<- Server templates
<- Static assets (CSS, images)
Babel Config
.babelrc
{
"presets": ["react", "es2015"]
}
Webpack config (webpack.config.js)
const path = require('path');
module.exports = {
entry: [
'./src/app-client.js',
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/',
},
module: {
rules: [
{
test: path.join(__dirname, 'src'),
use: { loader: 'babel-loader' },
},
],
},
};
Webpack config (webpack.config.js) - Add HMR !
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/app-client.js',
],
output: {/* ... */},
devServer: {
contentBase: path.join(__dirname, 'static'),
historyApiFallback: true,
port: 3000,
hot: true
},
module: {/* ... */},
plugins: [new webpack.HotModuleReplacementPlugin()]
};
static/index.html (for development)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Judo Heroes - A Universal JavaScript demo application with React</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="main"></div>
<script src="/static/bundle.js"></script>
</body>
</html>
Add static resources (CSS and images)
1. Get a .zip with the needed static resources from here: loige.link/jhw-static
2. Unzip
3. Copy the content of the unzipped folder (static) into your static folder
Add data file (We will need it later)
1. Get a .zip with the needed data file from here: loige.link/jhw-data
2. Unzip
3. Copy the content of the unzipped folder (src) into your src folder
Temporary src/app-client.js (just to test our local setup!)
import React from 'react';
import { render } from 'react-dom';
const AppClient = () => (
<h1>Hello Rovinj</h1>
);
window.onload = () => {
render(<AppClient />, document.getElementById('main'));
};
Run dev server
Run:
node_modules/.bin/webpack-dev-server
Now your project is available at http://localhost:3000
Try to change something in
src/app-client.js and save!
dev env is
finally ready!
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
15:15-15:30
☕
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Break the app into components
Let's see all the components we will need for this app!
● Layout
● IndexPage
● AthletePage
● NotFoundPage
● AthleteCard
● AthletesMenu
● Flag
● Medal
Flag component (src/components/Flag.js)
import React from 'react';
export const Flag = props => (
<span className="flag">
<img className="icon"
title={props.name}
src={`/img/${props.icon}`}
alt={`${props.name}'s flag`} />
{props.showName &&
<span className="name"> {props.name}</span>}
</span>
);
export default Flag;
Props:
● name (e.g. "France")
● icon (e.g. "flag-fr.png")
● showName (true|false)
<Flag
name="France"
icon="flag-fr.png"
showName={true}/>
Medal component (src/components/Medal.js)
import React from 'react';
export const medalTypes = {
G: 'Gold',
S: 'Silver',
B: 'Bronze',
};
export const Medal = props => (
<li className="medal">
<span
className={`symbol symbol-${props.type}`}
title={medalTypes[props.type]}
>
{props.type}
</span>
<span className="year">{props.year}</span>
<span className="city"> {props.city}</span>
<span className="event"> ({props.event})</span>
<span className="category"> {props.category}</span>
</li>
);
export default Medal;
Props:
● type ("G"|"S"|"B")
● year (e.g. "2017")
● city (e.g "Atlanta")
● event (e.g "Olympic Games")
● category (e.g "-86kg")
<Medal
type="G"
year="2017"
city="Atlanta"
event="Olympic Games"
category="-86kg"
/>
AthletesMenu component (src/components/AthletesMenu.js)
import React from 'react';
const shortName = (fullname) => {
const [name, surname] = fullname.split(' ');
return `${name[0]}. ${surname}`;
};
const AhtleteMenuLink = ({ to, label }) => (
<a href={to}>{label}</a>
);
export const AthletesMenu = ({ athletes }) => (
<nav className="atheletes-menu">
{ athletes.map(athlete =>
<AhtleteMenuLink
key={athlete.id}
to={`/athlete/${athlete.id}`}
label={shortName(athlete.name)}
/>
)}
</nav>
);
export default AthletesMenu;
Props:
● athletes (our data object)
import athletes from './data/athletes'
<AthletesMenu athletes={athletes}/>
AthleteCard component (src/components/AthleteCard.js)
import React from 'react';
export const AthleteCard = props => (
<a href={`/athlete/${props.id}`}>
<div className="athlete-preview">
<img
src={`img/${props.image}`}
alt={`${props.name}'s profile`} />
<h2 className="name">{props.name}</h2>
<span className="medals-count">
<img
src="/img/medal.png"
alt="Medal icon" /> {props.medals.length}
</span>
</div>
</a>
);
export default AthleteCard;
Props:
id, image, name, medals
(Attributes of an athlete in our data file)
import athletes from './data/athletes'
<AthleteCard {...athletes[0]}/>
Spread syntax:
Passes all the entries (key/values)
of the athletes[0] object as
props
IndexPage component (src/components/IndexPage.js)
import React from 'react';
import { AthleteCard } from './AthleteCard';
export const IndexPage = ({ athletes }) => (
<div className="home">
<div className="athletes-selector">
{athletes.map( athleteData =>
<AthleteCard
key={athleteData.id}
{...athleteData} />,
)}
</div>
</div>
);
export default IndexPage;
Props:
● athletes (our data object)
import athletes from './data/athletes'
<IndexPage athletes={athletes}/>
AthletePage component (src/components/AthletePage.js)
import React from 'react';
import { AthletesMenu } from './AthletesMenu';
import { Medal } from './Medal';
import { Flag } from './Flag';
export const AthletePage = ({ athlete, athletes }) => {
const headerStyle = { backgroundImage: `url(/img/${athlete.cover})` };
return (
<div className="athlete-full">
<AthletesMenu athletes={athletes} />
<div className="athlete">
<header style={headerStyle} />
<div className="picture-container">
<img alt={`${athlete.name}'s profile`} src={`/img/${athlete.image}`} />
<h2 className="name">{athlete.name}</h2>
</div>
<section className="description">
Olympic medalist from
&nbsp;<strong><Flag {...athlete.country} showName="true" /></strong>,
born in {athlete.birth}
(Find out more on <a href={athlete.link}>Wikipedia</a>).
</section>
<section className="medals">
<p>Winner of <strong>{athlete.medals.length}</strong> medals:</p>
<ul>{
athlete.medals.map(medal => <Medal key={medal.id} {...medal} />)
}</ul>
</section>
</div>
<div className="navigateBack">
<a href="/">« Back to the index</a>
</div>
</div>
);
}; export default AthletePage;
Props:
● athletes (our data object)
● athlete (the selected athlete)
import athletes from './data/athletes'
<AthletePage
athletes={athletes}
athlete={athletes[0]}/>
NotFoundPage component (src/components/NotFoundPage.js)
import React from 'react';
export const NotFoundPage = () => (
<div className="not-found">
<h1>404</h1>
<h2>Page not found!</h2>
<p>
<a href="/">Go back to the main page</a>
</p>
</div>
);
export default NotFoundPage;
Props: -
<NotFoundPage/>
Layout component (src/components/Layout.js)
import React from 'react';
export const Layout = props => (
<div className="app-container">
<header>
<a href="/">
<img className="logo"
src="/img/logo-judo-heroes.png"
alt="Judo Heroes logo" />
</a>
</header>
<div className="app-content">{props.children}</div>
<footer>
<p>
This is a demo app to showcase <strong>universal
Javascript</strong>
with <strong>React</strong> and
<strong>Express</strong>.
</p>
</footer>
</div>
);
export default Layout;
Props:
● children (the element to render as
main content)
<Layout>
<span>Your content here...</span>
</Layout>
How do we assemble our components
into a navigable prototype?
We need routing!
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
15:45
React Router (v4)
● Dynamic Routing:
Routing that takes place as your app is rendering
● Universal Routing:
Can resolve routes also while rendering on the server
● Advanced features:
○ Nested Routes
○ Responsive Routes
import React from 'react'
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
const Page = ({name}) => (<div><h1>{name}</h1></div>)
const Home = () => (<Page name="Home Page" />)
const Page1 = () => (<Page name="Page 1" />)
const Page2 = () => (<Page name="Page 2" />)
const Menu = () => (
<nav><ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/pages/page1">Page1</Link></li>
<li><Link to="/pages/page2">Page2</Link></li>
</ul></nav>
)
const App = () => (
<Router>
<div>
<Menu/>
<hr />
<Route exact path="/" component={Home}/>
<Route path="/pages/page1" component={Page1}/>
<Route path="/pages/page2" component={Page2} />
</div>
</Router>
)
render(<App />, document.getElementById('root'));
Router component wraps the app
Route component allows to
selectively render a component if the
current URL matches the path
Link component is used to create
dynamic hyperlinks
Base components
import React from 'react'
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
const Page = ({name}) => (<div><h1>{name}</h1></div>)
const Menu = () => (
<nav><ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/pages/page1">Page1</Link></li>
<li><Link to="/pages/page2">Page2</Link></li>
</ul></nav>
)
const App = () => (
<Router>
<div>
<Menu/>
<hr />
<Route exact path="/" render={() => <Page name="Home Page" />}/>
<Route path="/pages/page1" render={() => <Page name="Page 1" />}/>
<Route path="/pages/page2" render={() => <Page name="Page 2" />}/>
</div>
</Router>
)
render(<App />, document.getElementById('root'));
Alternative syntax with render prop
render prop syntax
import React from 'react'
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
const Page = ({name}) => (<div><h1>{name}</h1></div>)
const Menu = () => (
<nav><ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/pages/page1">Page1</Link></li>
<li><Link to="/pages/page2">Page2</Link></li>
</ul></nav>
)
const App = () => (
<Router>
<div>
<Menu/>
<hr />
<Switch>
<Route exact path="/" render={() => <Page name="Home Page" />}/>
<Route path="/pages/page1" render={() => <Page name="Page 1" />}/>
<Route path="/pages/page2" render={() => <Page name="Page 2" />}/>
<Route render={() => <Page name="NOT FOUND!" />}/>
</Switch>
</div>
</Router>
)
render(<App />, document.getElementById('root'));
Switch will render only the first route
that match (or the last if none match)
Switch and
default route
import React from 'react'
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
const Page = ({name}) => (<div><h1>{name}</h1></div>)
const Menu = () => (
<nav><ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/pages/page1">Page1</Link></li>
<li><Link to="/pages/page2">Page2</Link></li>
</ul></nav>
)
const App = () => (
<Router>
<div>
<Menu/>
<hr />
<Switch>
<Route exact path="/" render={() => <Page name="Home Page" />}/>
<Route path="/pages/:id"
render={({match}) => <Page name={match.params.id} />}/>
<Route render={() => <Page name="NOT FOUND!" />}/>
</Switch>
</div>
</Router>
)
render(<App />, document.getElementById('root'));
Route parameters can be
specified with :param syntax
Parameterized routes
Route components propagates the prop
match to the child component.
It contains all the params of the matched
URL.
✏ Exercise - Routing
Using React Router,
implements a basic blog application with
2 routes:
● / (home)
● /post/:id (specific post)
Bonus: Handle 404 pages
https://codesandbox.io/s/42711k5xn0
Add react-router-dom
as dependency
16:10
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
16:10
Let's define our routes
● IndexPage: /
● AthletePage: /athlete/:id
● NotFoundPage: Everything else
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Layout } from './Layout';
import { IndexPage } from './IndexPage';
import { AthletePage } from './AthletePage';
import { NotFoundPage } from './NotFoundPage';
import athletes from '../data/athletes';
const renderIndex = () => <IndexPage athletes={athletes} />;
const renderAthlete = ({ match, staticContext }) => {
const id = match.params.id;
const athlete = athletes.find(current => current.id === id);
if (!athlete) {
return <NotFoundPage staticContext={staticContext} />;
}
return <AthletePage athlete={athlete} athletes={athletes} />;
};
export const App = () => (
<Layout>
<Switch>
<Route exact path="/" render={renderIndex} />
<Route exact path="/athlete/:id" render={renderAthlete} />
<Route component={NotFoundPage} />
</Switch>
</Layout>
);
export default App;
Assemble client app
src/components/App.js
Update src/app-client.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './components/App'
const AppClient = () => (
<Router><App/></Router>
);
window.onload = () => {
render(<AppClient />, document.getElementById('main'));
};
Let's test it
Clicking links makes the page refresh!
We need to use the <Link> component!
16:20
src/components/AthleteCard.js
import React from 'react';
export const AthleteCard = props => (
<a href={`/athlete/${props.id}`}>
…
</a>
);
export default AthleteCard;
import React from 'react';
import { Link } from 'react-router-dom';
export const AthleteCard = props => (
<Link to={`/athlete/${props.id}`}>
…
</Link>
);
export default AthleteCard;
src/components/AthletePage.js
import React from 'react';
export const AthletePage = ({ athlete, athletes }) => {
const headerStyle = { backgroundImage:
`url(/img/${athlete.cover})` };
return (
…
<a href="/">« Back to the index</a>
…
);
};
export default AthletePage;
import React from 'react';
import { Link } from 'react-router-dom';
export const AthletePage = ({ athlete, athletes }) =>
{
const headerStyle = { backgroundImage:
`url(/img/${athlete.cover})` };
return (
…
<Link to="/">« Back to the index</Link>
…
);
};
export default AthletePage;
src/components/AthletesMenu.js
import React from 'react';
…
const AhtleteMenuLink = ({ to, label }) => (
<a href={to}>{label}</a>
);
…
export default AthletesMenu;
import React from 'react';
import { Link } from 'react-router-dom';
…
const AhtleteMenuLink = ({ to, label }) => (
<Link to={to}>{label}</Link>
);
…
export default AthletesMenu;
src/components/Layout.js
import React from 'react';
export const Layout = props => (
<div className="app-container">
<header>
<a href="/">
<img
className="logo"
src="/img/logo-judo-heroes.png"
alt="Judo Heroes logo" />
</a>
</header>
…
</div>
);
export default Layout;
import React from 'react';
import { Link } from 'react-router-dom';
export const Layout = props => (
<div className="app-container">
<header>
<Link to="/">
<img
className="logo"
src="/img/logo-judo-heroes.png"
alt="Judo Heroes logo" />
</Link>
</header>
…
</div>
);
export default Layout;
src/components/NotFoundPage.js
import React from 'react';
export const NotFoundPage = () => (
<div className="not-found">
<h1>404</h1>
<h2>Page not found!</h2>
<p>
<a href="/">Go back to the main page</a>
</p>
</div>
);
export default NotFoundPage;
import React from 'react';
import { Link } from 'react-router-dom';
export const NotFoundPage = () => (
<div className="not-found">
<h1>404</h1>
<h2>Page not found!</h2>
<p>
<Link to="/">Go back to the main page</Link>
</p>
</div>
);
export default NotFoundPage;
Now everything should be fine!
16:30
Extra: mark the current menu item as active
src/components/AthletesMenu.js
import React from 'react';
import { Link } from 'react-router-dom';
…
const AhtleteMenuLink = ({ to, label }) => (
<Link to={to}>{label}</Link>
);
…
export default AthletesMenu;
import React from 'react';
import { Link, Route } from 'react-router-dom';
…
const AhtleteMenuLink = ({ to, label }) => (
<Route path={to}>
{({ match }) => (
<Link to={to} className={match ? 'active' : ''}>
{label}
</Link>
)}
</Route>
);
…
export default AthletesMenu;If we pass a function inside a Route we can render
content. match will be true if the current path matches
the route.
This is active!
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
16:30
React Server Side Rendering (SSR)
// src/testSSR.js
import React from 'react';
import { renderToString } from 'react-dom/server';
const SampleApp = () => (
<h1>Hello World</h1>
);
console.log(renderToString(<SampleApp/>));
node_modules/.bin/babel-node src/testSSR.js
Let's render our app
// src/testSSR.js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter as Router } from 'react-router-dom';
import { App } from './components/App';
const ServerApp = () => (
<Router location="/" context={{}}>
<App />
</Router>
);
console.log(renderToString(<ServerApp/>));
StaticRouter is an implementation of React
Router that accepts the location path as a prop.
Server Side Rendering and Routing
We can create an Express server that can serve our pages with the React app already
rendered (based on the current URL)
We need a template first (src/views/index.ejs)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Judo Heroes - A Universal JavaScript demo application with React</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="main"><%- markup -%></div>
<script src="/bundle.js"></script>
</body>
</html>
This placeholder will be replaced with the markup
rendered with React on the server side
src/server.js
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter as Router } from 'react-router-dom';
import { App } from './components/App';
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(Express.static(path.join(__dirname, '..', 'dist')));
app.use(Express.static(path.join(__dirname, '..', 'static')));
app.get('*', (req, res) => {
const context = {};
const markup = renderToString(
<Router location={req.url} context={context}>
<App />
</Router>,
);
return res.render('index', { markup });
});
server.listen(3000, () => {
return console.info('Server running on http://localhost:3000');
});
Setup Express App with templating
and static assets
Universal routing and rendering.
req.url is used to pass the current
URL to React Router.
The resulting markup is embedded into
our template index.ejs and returned
as response.
Starts the server on the port 3000
16:45
Before we can start the server...
1. Delete static/index.html
(or it will be served when we visit the home, skipping SSR)
2. Restore original webpack config (no HMR)
3. Run webpack to regenerate the bundle file:
node_modules/.bin/webpack
// webpack.config.js
const path = require('path');
module.exports = {
entry: [
'./src/app-client.js',
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/',
},
module: {
rules: [
{
test: path.join(__dirname, 'src'),
use: { loader: 'babel-loader' },
},
],
},
};
Start the server
node_modules/.bin/babel-node src/server.js
We use babel-node because we are rendering
JSX and ES2015 modules in the server...
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
"BAD JUJU" for SEO!
How to report proper 404 status from the server?
By using the rendering context!
16:55
src/components/NotFoundPage.js
import React from 'react';
import { Link } from 'react-router-dom';
export const NotFoundPage = () => (
<div className="not-found">
<h1>404</h1>
<h2>Page not found!</h2>
<p>
<Link href="/">
Go back to the main page
</Link>
</p>
</div>
);
export default NotFoundPage;
import React from 'react';
import { Link } from 'react-router-dom';
export class NotFoundPage extends React.Component {
componentWillMount() {
const { staticContext } = this.props;
if (staticContext) {
staticContext.is404 = true;
}
}
render() {
return (<div className="not-found">
<h1>404</h1>
<h2>Page not found!</h2>
<p>
<Link to="/">Go back to the main page</Link>
</p>
</div>
);
}
}
export default NotFoundPage;
staticContext is available when rendering from
StaticRouter and allows components to exchange
arbitrary data will rendering
src/server.js
// ...
// universal routing and rendering
app.get('*', (req, res) => {
let status = 200;
const context = {};
const markup = renderToString(
<Router location={req.url} context={context}>
<App />
</Router>,
);
if (context.is404) {
status = 404;
}
return res.status(status).render('index', { markup });
});
// ...
context contains all the values that our components
share during rendering with the StaticRouter
Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Extra: Production build
Production build
● Our bundle needs to be minified
● React can be optimized too
● Babel-node is not good for production!
Run Webpack for production:
> webpack -p
"Webpacking" the server:
webpack.server.config.js
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node',
node: {
__dirname: false,
},
externals: [nodeExternals({
modulesFromFile: true,
})],
entry: {
js: './src/server.js',
},
output: {
path: path.join(__dirname, 'src'),
filename: 'server-es5.js',
libraryTarget: 'commonjs2',
},
module: {
rules: [{
test: path.join(__dirname, 'src'),
use: { loader: 'babel-loader' },
}],
},
};
Generating production files and start the app
// install webpack utility to compile the server
npm i webpack-node-externals
// build the client
node_modules/.bin/webpack -p
// build the server
node_modules/.bin/webpack -p --config webpack.server.config.js
// start the server
node src/server-es5.js
Agenda
● Introduction to Universal JavaScript
● Today's app "preview"
● A React primer
● Setup local development environment
● Break the app into components
● React Router primer
● Add routing to our app
● Make the app universal
● Production build
Useful resources
● Full chapter in Node.Js design patterns about Universal JavaScript
(remember the discount )
● Create React App
● Universal Create React App
● Progressive Web Apps with React
● React/Redux Universal boilerplate with HMR
● The code for Judo Heroes (V2) - Remember to STAR this repo
Huge thanks to @andreaman87 and @quasi_modal (Follow them on Twitter!)
FEEDBACK TIME
https://joind.in/talk/85338

More Related Content

PDF
Redux vs Alt
PDF
GWT integration with Vaadin
PPTX
PPTX
Why react is different
PPTX
Redux training
PDF
Frontend meetup 2014.06.25
PDF
Webcomponents at Frontend meetup 2014.06.25
PDF
Basic Tutorial of React for Programmers
Redux vs Alt
GWT integration with Vaadin
Why react is different
Redux training
Frontend meetup 2014.06.25
Webcomponents at Frontend meetup 2014.06.25
Basic Tutorial of React for Programmers

What's hot (20)

PDF
Quick start with React | DreamLab Academy #2
PPTX
Introduction to react and redux
KEY
RESTfull with RestKit
PPTX
React with Redux
PDF
Graphql, REST and Apollo
PDF
React, Redux, ES2015 by Max Petruck
PDF
Angular 2 introduction
PPTX
Web components
PDF
React lecture
PPTX
SharePoint Conference 2018 - APIs, APIs everywhere!
PDF
Intro to Redux | DreamLab Academy #3
PDF
GR8Conf 2011: Grails Webflow
PDF
A full introductory guide to React
PDF
Intro to React | DreamLab Academy
PDF
React + Redux. Best practices
PDF
To-Do App With Flutter: Step By Step Guide
PDF
MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"
PPTX
Grails transactions
PDF
React & Redux
PDF
Under the Hood: Using Spring in Grails
Quick start with React | DreamLab Academy #2
Introduction to react and redux
RESTfull with RestKit
React with Redux
Graphql, REST and Apollo
React, Redux, ES2015 by Max Petruck
Angular 2 introduction
Web components
React lecture
SharePoint Conference 2018 - APIs, APIs everywhere!
Intro to Redux | DreamLab Academy #3
GR8Conf 2011: Grails Webflow
A full introductory guide to React
Intro to React | DreamLab Academy
React + Redux. Best practices
To-Do App With Flutter: Step By Step Guide
MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"
Grails transactions
React & Redux
Under the Hood: Using Spring in Grails
Ad

Similar to Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop) (20)

PPT
PDF
Fundamental concepts of react js
PPTX
React_Complete.pptx
PDF
Getting Started with React, When You’re an Angular Developer
PDF
Fundamental Concepts of React JS for Beginners.pdf
PDF
React js
PPTX
slides.pptx
PPTX
slides.pptx
PDF
Intro to react_v2
PDF
react hook and wesite making structure ppt
PPTX
React & Redux for noobs
PDF
ReactJS for Programmers
PDF
Building web applications with React (Jfokus)
PPTX
Reactjs notes.pptx for web development- tutorial and theory
PPTX
Dyanaimcs of business and economics unit 2
PPTX
Intro to React.js
PDF
Introduction to React JS
PDF
Full Stack React Workshop [CSSC x GDSC]
PDF
theory-slides-vueh3urh4ur4ur4r44oirj4riu4ri
PPTX
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
Fundamental concepts of react js
React_Complete.pptx
Getting Started with React, When You’re an Angular Developer
Fundamental Concepts of React JS for Beginners.pdf
React js
slides.pptx
slides.pptx
Intro to react_v2
react hook and wesite making structure ppt
React & Redux for noobs
ReactJS for Programmers
Building web applications with React (Jfokus)
Reactjs notes.pptx for web development- tutorial and theory
Dyanaimcs of business and economics unit 2
Intro to React.js
Introduction to React JS
Full Stack React Workshop [CSSC x GDSC]
theory-slides-vueh3urh4ur4ur4r44oirj4riu4ri
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
Ad

More from Luciano Mammino (20)

PDF
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the ben...
PDF
Did you know JavaScript has iterators? DublinJS
PDF
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
PDF
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
PDF
From Node.js to Design Patterns - BuildPiper
PDF
Let's build a 0-cost invite-only website with Next.js and Airtable!
PDF
Everything I know about S3 pre-signed URLs
PDF
Serverless for High Performance Computing
PDF
Serverless for High Performance Computing
PDF
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
PDF
Building an invite-only microsite with Next.js & Airtable
PDF
Let's take the monolith to the cloud 🚀
PDF
A look inside the European Covid Green Certificate - Rust Dublin
PDF
Monoliths to the cloud!
PDF
The senior dev
PDF
Node.js: scalability tips - Azure Dev Community Vijayawada
PDF
A look inside the European Covid Green Certificate (Codemotion 2021)
PDF
AWS Observability Made Simple
PDF
Semplificare l'observability per progetti Serverless
PDF
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the ben...
Did you know JavaScript has iterators? DublinJS
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
From Node.js to Design Patterns - BuildPiper
Let's build a 0-cost invite-only website with Next.js and Airtable!
Everything I know about S3 pre-signed URLs
Serverless for High Performance Computing
Serverless for High Performance Computing
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
Building an invite-only microsite with Next.js & Airtable
Let's take the monolith to the cloud 🚀
A look inside the European Covid Green Certificate - Rust Dublin
Monoliths to the cloud!
The senior dev
Node.js: scalability tips - Azure Dev Community Vijayawada
A look inside the European Covid Green Certificate (Codemotion 2021)
AWS Observability Made Simple
Semplificare l'observability per progetti Serverless
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021

Recently uploaded (20)

PDF
AI in Product Development-omnex systems
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPTX
ai tools demonstartion for schools and inter college
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
System and Network Administraation Chapter 3
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
top salesforce developer skills in 2025.pdf
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
Introduction to Artificial Intelligence
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
medical staffing services at VALiNTRY
AI in Product Development-omnex systems
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
ai tools demonstartion for schools and inter college
Wondershare Filmora 15 Crack With Activation Key [2025
Operating system designcfffgfgggggggvggggggggg
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PTS Company Brochure 2025 (1).pdf.......
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Navsoft: AI-Powered Business Solutions & Custom Software Development
System and Network Administraation Chapter 3
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
CHAPTER 2 - PM Management and IT Context
Internet Downloader Manager (IDM) Crack 6.42 Build 41
top salesforce developer skills in 2025.pdf
How to Migrate SBCGlobal Email to Yahoo Easily
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Introduction to Artificial Intelligence
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
medical staffing services at VALiNTRY

Universal JS Web Applications with React - Web Summer Camp 2017, Rovinj (Workshop)

  • 1. Universal JS Web Applications with React Luciano Mammino (@loige) Photo by Ryan Holloway on Unsplash loige.link/uni-js-workshop ROVINJ, Aug 30 2017
  • 2. Who is Luciano? Principal Application Engineer Connect Website - Twitter - GitHub - Linkedin fullstackbulletin.com -15% Print (NpBK15WSCR) -20% eBook (NeBK20WSCR)
  • 3. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 13:50
  • 5. NOT ONLY FOR THE WEB... Desktop applications Mobile applications Hardware!
  • 6. ADVANTAGES OF UNIVERSAL JAVASCRIPT "JavaScript-only" development Maintainability Better SEO Faster "perceived" load time
  • 7. MOAR ADVANTAGES... Keep using React/JS paradigms for "static" websites Speed up content loading with linkprefetch loige.link/universal-react-made-easy-talk
  • 10. MODULE SHARING Use Node.js modules in the browser
  • 11. UNIVERSAL RENDERING Render the views of the application from the server (first request) and then in the browser (next requests)
  • 12. UNIVERSAL ROUTING Recognise the view associated to the current route from both the server and the browser.
  • 13. UNIVERSAL DATA RETRIEVAL Access data (and APIs) from both the server and the browser. AXIOS UNIVERSAL FETCH
  • 14. UNIVERSAL STATE Manage changes on the state tree both on the server and the client...
  • 17. OK… let's stop complaining and start coding!
  • 18. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 14:00
  • 19. WHAT ARE WE GOING TO BUILD? loige.link/judo-heroes-app​ loige.link/judo-heroes-tutorial
  • 25. WHAT TOOLS ARE WE GOING TO USE?
  • 26. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 14:05
  • 27. A React primer ● Components ● JSX ● Composition ● Props ● Events & State
  • 28. Everything is a component ● The view is made up by a tree of components (like the DOM is made up by a tree of HTML tags) ● Components can be created in different ways (pure functions, class syntax) ● A component can include other components as children ● Components content is defined in JSX ● Components can receive data from the outside (props) ● Components can manage internal data (state) ● Components can be installed (rendered) in an HTML page ● Generally you'll have one main component containing all the others in your page
  • 30. Pure function syntax import React from 'react'; import { render } from 'react-dom'; const IAmAComponent = () => ( <div> <h1>This is a component as a pure function</h1> </div> ); render(<IAmAComponent />, document.getElementById('root'));
  • 31. Class extend syntax import React from 'react'; import { render } from 'react-dom'; class IAmAnotherComponent extends React.Component { render() { return ( <div> <h1>This is a component created with ES6 classes</h1> </div> ) } } render(<IAmAnotherComponent />, document.getElementById('root'));
  • 32. ✏ Exercise - HelloWorld component Create an HelloWorld component that prints "Hello World" Render the HelloWorld component on the page https://codesandbox.io/s/p34lnpm6q0 14:20
  • 33. JSX ● It looks like HTML/XML, considered a superset of JavaScript ● It can contain JavaScript expressions ● It is "transpiled" to plain JavaScript using Babel ● Can include regular HTML tags (they start with a lowercase character) ● … or other components (they start with an uppercase character) ● Can include regular HTML attributes … With some exceptions: ○ class becomes className ○ properties with hyphens becomes camelcase (background-color -> backgroundColor)
  • 34. <div className="shopping-list"> <ul> <li>Mushrooms</li> <li>Lasagna</li> </ul> </div> React.createElement( "div", { className: "shopping-list" }, React.createElement( "ul", null, React.createElement( "li", null, "Mushrooms" ), React.createElement( "li", null, "Lasagna" ) ) ); JSX Compiled JS (By Babel/React Transpiler)
  • 35. JSX examples import React from 'react'; import { render } from 'react-dom'; const SampleComponent = () => ( <h1> Last render time: <strong style={{backgroundColor: 'yellow', padding: '2px'}}> {(new Date).toISOString()} </strong> </h1> ) render(<SampleComponent />, document.getElementById('root'));
  • 37. Components Tree import React from 'react'; import { render } from 'react-dom'; const FirstChildComponent = () => ( <div style={{ background: 'peru', padding: '2px' }}> <h2>FirstChildComponent</h2> </div> ) const SecondChildComponent = () => ( <div style={{ background: 'aqua', padding: '2px' }}> <h2>SecondChildComponent</h2> </div> ) const ParentComponent = () => ( <div style={{ border: '2px dotted black', padding: '4px'}}> <h1>ParentComponent</h1> <FirstChildComponent/> <SecondChildComponent/> </div> ) render(<ParentComponent />, document.getElementById('root'));
  • 38. ✏ Exercise - Combine components Create a react app with 3 components ● Title component ● Content component ● Footer component Then create a component called Layout that contains all the 3 as shown aside Then render the Layout component on the page https://codesandbox.io/s/48ok2yv3z7 14:35
  • 39. Props ● Attributes set to components are called Props ● They are used to pass "input" data into components (from the parent component) ● They allow to create "reusable" components import React from 'react'; import { render } from 'react-dom'; const PrintProps = (props) => ( <div> <h1>Received props</h1> <pre>{ JSON.stringify(props, null, 2) }</pre> </div> ) render(<PrintProps foo="bar" bar="baz" qoo="qoo" />, document.getElementById('root'));
  • 40. Reusable components import React from 'react'; import { render } from 'react-dom'; const ChildComponent = (props) => ( <div style={{ background: props.color, padding: '2px' }}> <h2>{props.name}</h2> </div> ) const ParentComponent = () => ( <div style={{ border: '2px dotted black', padding: '4px'}}> <h1>ParentComponent</h1> <ChildComponent color="peru" name="FirstChildComponent"/> <ChildComponent color="aqua" name="SecondChildComponent"/> </div> ) render(<ParentComponent />, document.getElementById('root'));
  • 41. ✏ Exercise - HelloWorld component with props Create an HelloWorld component that prints "Hello NAME", where NAME is coming from a props called name Render the HelloWorld component on the page passing your name as prop https://codesandbox.io/s/5vmr4o2m2n 14:45
  • 42. Decorator components import React from 'react'; import { render } from 'react-dom'; const Center = (props) => ( <div style={{ margin: 'auto', width: '50%', border: '2px solid #ccc', textAlign: 'center' }}> {props.children} </div> ) const HelloWorld = ({name = 'World'}) => (<h1>Hello {name}</h1>) const App = () => ( <Center> <HelloWorld name="Rovinj"/> </Center> ) render(<App />, document.getElementById('root'));
  • 43. Iterations import React from 'react'; import { render } from 'react-dom'; const difficoultThings = [ 'Naming things', 'Cache invalidation', 'Off by one errors' ]; const App = () => ( <div> <h2>The 2 most difficoult things in IT</h2> <ol> {difficoultThings.map((thing) => ( <li>{thing}</li> ) )} </ol> </div> ); render(<App />, document.getElementById('root'));
  • 44. Conditional rendering import React from 'react'; import { render } from 'react-dom'; const money = 220; const latestTransactions = [ { description: 'Restaurant', amount: 50 }, { description: 'Coffee', amount: 2 } ] const Transactions = ({data}) => { if (!data.length) { return (<div>No transaction available</div>) } return ( <div> <h3>Latest transactions</h3> <ul> { data.map((transaction) => ( <li>{transaction.amount} ({transaction.description})</li> )) } </ul> </div> ) } const App = () => ( <div> <h2>You have { money > 0 ? 'some': 'no' } Money!</h2> <p>Current Balance: {money}</p> <Transactions data={latestTransactions}/> </div> ); render(<App />, document.getElementById('root'));
  • 45. ✏ Exercise - Your favourite CSS colors Create a list of your favourite CSS colors. Hint: create a component to visualize a single color and render it multiple times based on the data contained in an array of colors. Bonus: Do something special with the color "RebeccaPurple". https://codesandbox.io/s/qqqz0n5z19 14:50
  • 46. Events & state import React from 'react'; import { render } from 'react-dom'; const rnd = () => Math.round(Math.random() * 255) class RandomColor extends React.Component { constructor(props) { super(props); this.state = { color: props.startColor || 'red' }; } changeColor() { const color = `rgb(${rnd()}, ${rnd()}, ${rnd()})` this.setState({color}) } render() { return ( <div onClick={this.changeColor.bind(this)} style={{ padding: '80px', textAlign: 'center', background: this.state.color }} >Click me!</div> ) } } render(<RandomColor startColor="RebeccaPurple" />, document.getElementById('root'));
  • 47. Quick Recap ● Everything is a component ● Composition over inheritance ● Pass application data through props ("data flows down") ● Components internal data constitutes state ● Components can react to events
  • 48. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 15:00
  • 49. Setup local dev environment mkdir judo-heroes-2 cd judo-heroes-2 npm init -y
  • 50. Install dependencies npm i --save babel-cli@6.18.0 babel-core@6.18.2 babel-loader@7.1.2 babel-preset-es2015@6.18.0 babel-preset-react@6.16.0 ejs@2.5.2 express@5.0.0-alpha.5 react@15.4.2 react-dom@15.4.2 react-router-dom@4.0.0 webpack@2.7.0 webpack-dev-server@2.7.1
  • 51. Folders structure mkdir -p src/components src/data src/views static <- React components <- Data file <- Server templates <- Static assets (CSS, images)
  • 53. Webpack config (webpack.config.js) const path = require('path'); module.exports = { entry: [ './src/app-client.js', ], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', publicPath: '/static/', }, module: { rules: [ { test: path.join(__dirname, 'src'), use: { loader: 'babel-loader' }, }, ], }, };
  • 54. Webpack config (webpack.config.js) - Add HMR ! const path = require('path'); const webpack = require('webpack'); module.exports = { entry: [ 'webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server', './src/app-client.js', ], output: {/* ... */}, devServer: { contentBase: path.join(__dirname, 'static'), historyApiFallback: true, port: 3000, hot: true }, module: {/* ... */}, plugins: [new webpack.HotModuleReplacementPlugin()] };
  • 55. static/index.html (for development) <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Judo Heroes - A Universal JavaScript demo application with React</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> <div id="main"></div> <script src="/static/bundle.js"></script> </body> </html>
  • 56. Add static resources (CSS and images) 1. Get a .zip with the needed static resources from here: loige.link/jhw-static 2. Unzip 3. Copy the content of the unzipped folder (static) into your static folder
  • 57. Add data file (We will need it later) 1. Get a .zip with the needed data file from here: loige.link/jhw-data 2. Unzip 3. Copy the content of the unzipped folder (src) into your src folder
  • 58. Temporary src/app-client.js (just to test our local setup!) import React from 'react'; import { render } from 'react-dom'; const AppClient = () => ( <h1>Hello Rovinj</h1> ); window.onload = () => { render(<AppClient />, document.getElementById('main')); };
  • 59. Run dev server Run: node_modules/.bin/webpack-dev-server Now your project is available at http://localhost:3000 Try to change something in src/app-client.js and save!
  • 61. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 15:15-15:30 ☕
  • 62. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 63. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 64. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 65. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 66. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 67. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 68. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 69. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 70. Break the app into components Let's see all the components we will need for this app! ● Layout ● IndexPage ● AthletePage ● NotFoundPage ● AthleteCard ● AthletesMenu ● Flag ● Medal
  • 71. Flag component (src/components/Flag.js) import React from 'react'; export const Flag = props => ( <span className="flag"> <img className="icon" title={props.name} src={`/img/${props.icon}`} alt={`${props.name}'s flag`} /> {props.showName && <span className="name"> {props.name}</span>} </span> ); export default Flag; Props: ● name (e.g. "France") ● icon (e.g. "flag-fr.png") ● showName (true|false) <Flag name="France" icon="flag-fr.png" showName={true}/>
  • 72. Medal component (src/components/Medal.js) import React from 'react'; export const medalTypes = { G: 'Gold', S: 'Silver', B: 'Bronze', }; export const Medal = props => ( <li className="medal"> <span className={`symbol symbol-${props.type}`} title={medalTypes[props.type]} > {props.type} </span> <span className="year">{props.year}</span> <span className="city"> {props.city}</span> <span className="event"> ({props.event})</span> <span className="category"> {props.category}</span> </li> ); export default Medal; Props: ● type ("G"|"S"|"B") ● year (e.g. "2017") ● city (e.g "Atlanta") ● event (e.g "Olympic Games") ● category (e.g "-86kg") <Medal type="G" year="2017" city="Atlanta" event="Olympic Games" category="-86kg" />
  • 73. AthletesMenu component (src/components/AthletesMenu.js) import React from 'react'; const shortName = (fullname) => { const [name, surname] = fullname.split(' '); return `${name[0]}. ${surname}`; }; const AhtleteMenuLink = ({ to, label }) => ( <a href={to}>{label}</a> ); export const AthletesMenu = ({ athletes }) => ( <nav className="atheletes-menu"> { athletes.map(athlete => <AhtleteMenuLink key={athlete.id} to={`/athlete/${athlete.id}`} label={shortName(athlete.name)} /> )} </nav> ); export default AthletesMenu; Props: ● athletes (our data object) import athletes from './data/athletes' <AthletesMenu athletes={athletes}/>
  • 74. AthleteCard component (src/components/AthleteCard.js) import React from 'react'; export const AthleteCard = props => ( <a href={`/athlete/${props.id}`}> <div className="athlete-preview"> <img src={`img/${props.image}`} alt={`${props.name}'s profile`} /> <h2 className="name">{props.name}</h2> <span className="medals-count"> <img src="/img/medal.png" alt="Medal icon" /> {props.medals.length} </span> </div> </a> ); export default AthleteCard; Props: id, image, name, medals (Attributes of an athlete in our data file) import athletes from './data/athletes' <AthleteCard {...athletes[0]}/> Spread syntax: Passes all the entries (key/values) of the athletes[0] object as props
  • 75. IndexPage component (src/components/IndexPage.js) import React from 'react'; import { AthleteCard } from './AthleteCard'; export const IndexPage = ({ athletes }) => ( <div className="home"> <div className="athletes-selector"> {athletes.map( athleteData => <AthleteCard key={athleteData.id} {...athleteData} />, )} </div> </div> ); export default IndexPage; Props: ● athletes (our data object) import athletes from './data/athletes' <IndexPage athletes={athletes}/>
  • 76. AthletePage component (src/components/AthletePage.js) import React from 'react'; import { AthletesMenu } from './AthletesMenu'; import { Medal } from './Medal'; import { Flag } from './Flag'; export const AthletePage = ({ athlete, athletes }) => { const headerStyle = { backgroundImage: `url(/img/${athlete.cover})` }; return ( <div className="athlete-full"> <AthletesMenu athletes={athletes} /> <div className="athlete"> <header style={headerStyle} /> <div className="picture-container"> <img alt={`${athlete.name}'s profile`} src={`/img/${athlete.image}`} /> <h2 className="name">{athlete.name}</h2> </div> <section className="description"> Olympic medalist from &nbsp;<strong><Flag {...athlete.country} showName="true" /></strong>, born in {athlete.birth} (Find out more on <a href={athlete.link}>Wikipedia</a>). </section> <section className="medals"> <p>Winner of <strong>{athlete.medals.length}</strong> medals:</p> <ul>{ athlete.medals.map(medal => <Medal key={medal.id} {...medal} />) }</ul> </section> </div> <div className="navigateBack"> <a href="/">« Back to the index</a> </div> </div> ); }; export default AthletePage; Props: ● athletes (our data object) ● athlete (the selected athlete) import athletes from './data/athletes' <AthletePage athletes={athletes} athlete={athletes[0]}/>
  • 77. NotFoundPage component (src/components/NotFoundPage.js) import React from 'react'; export const NotFoundPage = () => ( <div className="not-found"> <h1>404</h1> <h2>Page not found!</h2> <p> <a href="/">Go back to the main page</a> </p> </div> ); export default NotFoundPage; Props: - <NotFoundPage/>
  • 78. Layout component (src/components/Layout.js) import React from 'react'; export const Layout = props => ( <div className="app-container"> <header> <a href="/"> <img className="logo" src="/img/logo-judo-heroes.png" alt="Judo Heroes logo" /> </a> </header> <div className="app-content">{props.children}</div> <footer> <p> This is a demo app to showcase <strong>universal Javascript</strong> with <strong>React</strong> and <strong>Express</strong>. </p> </footer> </div> ); export default Layout; Props: ● children (the element to render as main content) <Layout> <span>Your content here...</span> </Layout>
  • 79. How do we assemble our components into a navigable prototype? We need routing!
  • 80. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 15:45
  • 81. React Router (v4) ● Dynamic Routing: Routing that takes place as your app is rendering ● Universal Routing: Can resolve routes also while rendering on the server ● Advanced features: ○ Nested Routes ○ Responsive Routes
  • 82. import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link } from 'react-router-dom' const Page = ({name}) => (<div><h1>{name}</h1></div>) const Home = () => (<Page name="Home Page" />) const Page1 = () => (<Page name="Page 1" />) const Page2 = () => (<Page name="Page 2" />) const Menu = () => ( <nav><ul> <li><Link to="/">Home</Link></li> <li><Link to="/pages/page1">Page1</Link></li> <li><Link to="/pages/page2">Page2</Link></li> </ul></nav> ) const App = () => ( <Router> <div> <Menu/> <hr /> <Route exact path="/" component={Home}/> <Route path="/pages/page1" component={Page1}/> <Route path="/pages/page2" component={Page2} /> </div> </Router> ) render(<App />, document.getElementById('root')); Router component wraps the app Route component allows to selectively render a component if the current URL matches the path Link component is used to create dynamic hyperlinks Base components
  • 83. import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link } from 'react-router-dom' const Page = ({name}) => (<div><h1>{name}</h1></div>) const Menu = () => ( <nav><ul> <li><Link to="/">Home</Link></li> <li><Link to="/pages/page1">Page1</Link></li> <li><Link to="/pages/page2">Page2</Link></li> </ul></nav> ) const App = () => ( <Router> <div> <Menu/> <hr /> <Route exact path="/" render={() => <Page name="Home Page" />}/> <Route path="/pages/page1" render={() => <Page name="Page 1" />}/> <Route path="/pages/page2" render={() => <Page name="Page 2" />}/> </div> </Router> ) render(<App />, document.getElementById('root')); Alternative syntax with render prop render prop syntax
  • 84. import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom' const Page = ({name}) => (<div><h1>{name}</h1></div>) const Menu = () => ( <nav><ul> <li><Link to="/">Home</Link></li> <li><Link to="/pages/page1">Page1</Link></li> <li><Link to="/pages/page2">Page2</Link></li> </ul></nav> ) const App = () => ( <Router> <div> <Menu/> <hr /> <Switch> <Route exact path="/" render={() => <Page name="Home Page" />}/> <Route path="/pages/page1" render={() => <Page name="Page 1" />}/> <Route path="/pages/page2" render={() => <Page name="Page 2" />}/> <Route render={() => <Page name="NOT FOUND!" />}/> </Switch> </div> </Router> ) render(<App />, document.getElementById('root')); Switch will render only the first route that match (or the last if none match) Switch and default route
  • 85. import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom' const Page = ({name}) => (<div><h1>{name}</h1></div>) const Menu = () => ( <nav><ul> <li><Link to="/">Home</Link></li> <li><Link to="/pages/page1">Page1</Link></li> <li><Link to="/pages/page2">Page2</Link></li> </ul></nav> ) const App = () => ( <Router> <div> <Menu/> <hr /> <Switch> <Route exact path="/" render={() => <Page name="Home Page" />}/> <Route path="/pages/:id" render={({match}) => <Page name={match.params.id} />}/> <Route render={() => <Page name="NOT FOUND!" />}/> </Switch> </div> </Router> ) render(<App />, document.getElementById('root')); Route parameters can be specified with :param syntax Parameterized routes Route components propagates the prop match to the child component. It contains all the params of the matched URL.
  • 86. ✏ Exercise - Routing Using React Router, implements a basic blog application with 2 routes: ● / (home) ● /post/:id (specific post) Bonus: Handle 404 pages https://codesandbox.io/s/42711k5xn0 Add react-router-dom as dependency 16:10
  • 87. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 16:10
  • 88. Let's define our routes ● IndexPage: / ● AthletePage: /athlete/:id ● NotFoundPage: Everything else
  • 89. import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Layout } from './Layout'; import { IndexPage } from './IndexPage'; import { AthletePage } from './AthletePage'; import { NotFoundPage } from './NotFoundPage'; import athletes from '../data/athletes'; const renderIndex = () => <IndexPage athletes={athletes} />; const renderAthlete = ({ match, staticContext }) => { const id = match.params.id; const athlete = athletes.find(current => current.id === id); if (!athlete) { return <NotFoundPage staticContext={staticContext} />; } return <AthletePage athlete={athlete} athletes={athletes} />; }; export const App = () => ( <Layout> <Switch> <Route exact path="/" render={renderIndex} /> <Route exact path="/athlete/:id" render={renderAthlete} /> <Route component={NotFoundPage} /> </Switch> </Layout> ); export default App; Assemble client app src/components/App.js
  • 90. Update src/app-client.js import React from 'react'; import { render } from 'react-dom'; import { BrowserRouter as Router } from 'react-router-dom'; import App from './components/App' const AppClient = () => ( <Router><App/></Router> ); window.onload = () => { render(<AppClient />, document.getElementById('main')); };
  • 92. Clicking links makes the page refresh! We need to use the <Link> component! 16:20
  • 93. src/components/AthleteCard.js import React from 'react'; export const AthleteCard = props => ( <a href={`/athlete/${props.id}`}> … </a> ); export default AthleteCard; import React from 'react'; import { Link } from 'react-router-dom'; export const AthleteCard = props => ( <Link to={`/athlete/${props.id}`}> … </Link> ); export default AthleteCard;
  • 94. src/components/AthletePage.js import React from 'react'; export const AthletePage = ({ athlete, athletes }) => { const headerStyle = { backgroundImage: `url(/img/${athlete.cover})` }; return ( … <a href="/">« Back to the index</a> … ); }; export default AthletePage; import React from 'react'; import { Link } from 'react-router-dom'; export const AthletePage = ({ athlete, athletes }) => { const headerStyle = { backgroundImage: `url(/img/${athlete.cover})` }; return ( … <Link to="/">« Back to the index</Link> … ); }; export default AthletePage;
  • 95. src/components/AthletesMenu.js import React from 'react'; … const AhtleteMenuLink = ({ to, label }) => ( <a href={to}>{label}</a> ); … export default AthletesMenu; import React from 'react'; import { Link } from 'react-router-dom'; … const AhtleteMenuLink = ({ to, label }) => ( <Link to={to}>{label}</Link> ); … export default AthletesMenu;
  • 96. src/components/Layout.js import React from 'react'; export const Layout = props => ( <div className="app-container"> <header> <a href="/"> <img className="logo" src="/img/logo-judo-heroes.png" alt="Judo Heroes logo" /> </a> </header> … </div> ); export default Layout; import React from 'react'; import { Link } from 'react-router-dom'; export const Layout = props => ( <div className="app-container"> <header> <Link to="/"> <img className="logo" src="/img/logo-judo-heroes.png" alt="Judo Heroes logo" /> </Link> </header> … </div> ); export default Layout;
  • 97. src/components/NotFoundPage.js import React from 'react'; export const NotFoundPage = () => ( <div className="not-found"> <h1>404</h1> <h2>Page not found!</h2> <p> <a href="/">Go back to the main page</a> </p> </div> ); export default NotFoundPage; import React from 'react'; import { Link } from 'react-router-dom'; export const NotFoundPage = () => ( <div className="not-found"> <h1>404</h1> <h2>Page not found!</h2> <p> <Link to="/">Go back to the main page</Link> </p> </div> ); export default NotFoundPage;
  • 98. Now everything should be fine! 16:30
  • 99. Extra: mark the current menu item as active src/components/AthletesMenu.js import React from 'react'; import { Link } from 'react-router-dom'; … const AhtleteMenuLink = ({ to, label }) => ( <Link to={to}>{label}</Link> ); … export default AthletesMenu; import React from 'react'; import { Link, Route } from 'react-router-dom'; … const AhtleteMenuLink = ({ to, label }) => ( <Route path={to}> {({ match }) => ( <Link to={to} className={match ? 'active' : ''}> {label} </Link> )} </Route> ); … export default AthletesMenu;If we pass a function inside a Route we can render content. match will be true if the current path matches the route. This is active!
  • 100. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build 16:30
  • 101. React Server Side Rendering (SSR) // src/testSSR.js import React from 'react'; import { renderToString } from 'react-dom/server'; const SampleApp = () => ( <h1>Hello World</h1> ); console.log(renderToString(<SampleApp/>)); node_modules/.bin/babel-node src/testSSR.js
  • 102. Let's render our app // src/testSSR.js import React from 'react'; import { renderToString } from 'react-dom/server'; import { StaticRouter as Router } from 'react-router-dom'; import { App } from './components/App'; const ServerApp = () => ( <Router location="/" context={{}}> <App /> </Router> ); console.log(renderToString(<ServerApp/>)); StaticRouter is an implementation of React Router that accepts the location path as a prop.
  • 103. Server Side Rendering and Routing We can create an Express server that can serve our pages with the React app already rendered (based on the current URL) We need a template first (src/views/index.ejs) <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Judo Heroes - A Universal JavaScript demo application with React</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> <div id="main"><%- markup -%></div> <script src="/bundle.js"></script> </body> </html> This placeholder will be replaced with the markup rendered with React on the server side
  • 104. src/server.js import path from 'path'; import { Server } from 'http'; import Express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { StaticRouter as Router } from 'react-router-dom'; import { App } from './components/App'; const app = new Express(); const server = new Server(app); app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); app.use(Express.static(path.join(__dirname, '..', 'dist'))); app.use(Express.static(path.join(__dirname, '..', 'static'))); app.get('*', (req, res) => { const context = {}; const markup = renderToString( <Router location={req.url} context={context}> <App /> </Router>, ); return res.render('index', { markup }); }); server.listen(3000, () => { return console.info('Server running on http://localhost:3000'); }); Setup Express App with templating and static assets Universal routing and rendering. req.url is used to pass the current URL to React Router. The resulting markup is embedded into our template index.ejs and returned as response. Starts the server on the port 3000 16:45
  • 105. Before we can start the server... 1. Delete static/index.html (or it will be served when we visit the home, skipping SSR) 2. Restore original webpack config (no HMR) 3. Run webpack to regenerate the bundle file: node_modules/.bin/webpack // webpack.config.js const path = require('path'); module.exports = { entry: [ './src/app-client.js', ], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', publicPath: '/static/', }, module: { rules: [ { test: path.join(__dirname, 'src'), use: { loader: 'babel-loader' }, }, ], }, };
  • 106. Start the server node_modules/.bin/babel-node src/server.js We use babel-node because we are rendering JSX and ES2015 modules in the server...
  • 108. "BAD JUJU" for SEO!
  • 109. How to report proper 404 status from the server? By using the rendering context! 16:55
  • 110. src/components/NotFoundPage.js import React from 'react'; import { Link } from 'react-router-dom'; export const NotFoundPage = () => ( <div className="not-found"> <h1>404</h1> <h2>Page not found!</h2> <p> <Link href="/"> Go back to the main page </Link> </p> </div> ); export default NotFoundPage; import React from 'react'; import { Link } from 'react-router-dom'; export class NotFoundPage extends React.Component { componentWillMount() { const { staticContext } = this.props; if (staticContext) { staticContext.is404 = true; } } render() { return (<div className="not-found"> <h1>404</h1> <h2>Page not found!</h2> <p> <Link to="/">Go back to the main page</Link> </p> </div> ); } } export default NotFoundPage; staticContext is available when rendering from StaticRouter and allows components to exchange arbitrary data will rendering
  • 111. src/server.js // ... // universal routing and rendering app.get('*', (req, res) => { let status = 200; const context = {}; const markup = renderToString( <Router location={req.url} context={context}> <App /> </Router>, ); if (context.is404) { status = 404; } return res.status(status).render('index', { markup }); }); // ... context contains all the values that our components share during rendering with the StaticRouter
  • 113. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Extra: Production build
  • 114. Production build ● Our bundle needs to be minified ● React can be optimized too ● Babel-node is not good for production! Run Webpack for production: > webpack -p
  • 115. "Webpacking" the server: webpack.server.config.js const path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { target: 'node', node: { __dirname: false, }, externals: [nodeExternals({ modulesFromFile: true, })], entry: { js: './src/server.js', }, output: { path: path.join(__dirname, 'src'), filename: 'server-es5.js', libraryTarget: 'commonjs2', }, module: { rules: [{ test: path.join(__dirname, 'src'), use: { loader: 'babel-loader' }, }], }, };
  • 116. Generating production files and start the app // install webpack utility to compile the server npm i webpack-node-externals // build the client node_modules/.bin/webpack -p // build the server node_modules/.bin/webpack -p --config webpack.server.config.js // start the server node src/server-es5.js
  • 117. Agenda ● Introduction to Universal JavaScript ● Today's app "preview" ● A React primer ● Setup local development environment ● Break the app into components ● React Router primer ● Add routing to our app ● Make the app universal ● Production build
  • 118. Useful resources ● Full chapter in Node.Js design patterns about Universal JavaScript (remember the discount ) ● Create React App ● Universal Create React App ● Progressive Web Apps with React ● React/Redux Universal boilerplate with HMR ● The code for Judo Heroes (V2) - Remember to STAR this repo
  • 119. Huge thanks to @andreaman87 and @quasi_modal (Follow them on Twitter!) FEEDBACK TIME https://joind.in/talk/85338