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?!
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
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...
"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
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

What's hot

Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2DreamLab
 
Introduction to react and redux
Introduction to react and reduxIntroduction to react and redux
Introduction to react and reduxCuong Ho
 
RESTfull with RestKit
RESTfull with RestKitRESTfull with RestKit
RESTfull with RestKitTaras Kalapun
 
React, Redux, ES2015 by Max Petruck
React, Redux, ES2015   by Max PetruckReact, Redux, ES2015   by Max Petruck
React, Redux, ES2015 by Max PetruckMaksym Petruk
 
SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!Sébastien Levert
 
Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 DreamLab
 
GR8Conf 2011: Grails Webflow
GR8Conf 2011: Grails WebflowGR8Conf 2011: Grails Webflow
GR8Conf 2011: Grails WebflowGR8Conf
 
A full introductory guide to React
A full introductory guide to ReactA full introductory guide to React
A full introductory guide to ReactJean Carlo Emer
 
Intro to React | DreamLab Academy
Intro to React | DreamLab AcademyIntro to React | DreamLab Academy
Intro to React | DreamLab AcademyDreamLab
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practicesClickky
 
MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"
MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"
MPD2011 | Сергей Клюев "RESTfull iOS with RestKit"ITGinGer
 
Grails transactions
Grails   transactionsGrails   transactions
Grails transactionsHusain Dalal
 
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsUnder the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsBurt Beckwith
 

What's hot (20)

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

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

Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Elyse Kolker Gordon
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux IntroductionNikolaus Graf
 
Academy PRO: React JS
Academy PRO: React JSAcademy PRO: React JS
Academy PRO: React JSBinary Studio
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and SymfonyIgnacio Martín
 
Introduction to React and MobX
Introduction to React and MobXIntroduction to React and MobX
Introduction to React and MobXAnjali Chawla
 
React: JSX and Top Level API
React: JSX and Top Level APIReact: JSX and Top Level API
React: JSX and Top Level APIFabio Biondi
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs[T]echdencias
 
React Native for multi-platform mobile applications
React Native for multi-platform mobile applicationsReact Native for multi-platform mobile applications
React Native for multi-platform mobile applicationsMatteo Manchi
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend DevelopersSergio Nakamura
 
Modern frontend development with VueJs
Modern frontend development with VueJsModern frontend development with VueJs
Modern frontend development with VueJsTudor Barbu
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for ProgrammersDavid Rodenas
 
Neoito — React 101
Neoito — React 101Neoito — React 101
Neoito — React 101Neoito
 
Build web apps with react js
Build web apps with react jsBuild web apps with react js
Build web apps with react jsdhanushkacnd
 
React & The Art of Managing Complexity
React &  The Art of Managing ComplexityReact &  The Art of Managing Complexity
React & The Art of Managing ComplexityRyan Anklam
 
Getting Started with React v16
Getting Started with React v16Getting Started with React v16
Getting Started with React v16Benny Neugebauer
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentYao Nien Chung
 

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

Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017Building Universal Web Apps with React ForwardJS 2017
Building Universal Web Apps with React ForwardJS 2017
 
React outbox
React outboxReact outbox
React outbox
 
React js
React jsReact js
React js
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux Introduction
 
Academy PRO: React JS
Academy PRO: React JSAcademy PRO: React JS
Academy PRO: React JS
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
Introduction to React and MobX
Introduction to React and MobXIntroduction to React and MobX
Introduction to React and MobX
 
React redux
React reduxReact redux
React redux
 
React: JSX and Top Level API
React: JSX and Top Level APIReact: JSX and Top Level API
React: JSX and Top Level API
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
Let's react - Meetup
Let's react - MeetupLet's react - Meetup
Let's react - Meetup
 
React Native for multi-platform mobile applications
React Native for multi-platform mobile applicationsReact Native for multi-platform mobile applications
React Native for multi-platform mobile applications
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend Developers
 
Modern frontend development with VueJs
Modern frontend development with VueJsModern frontend development with VueJs
Modern frontend development with VueJs
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for Programmers
 
Neoito — React 101
Neoito — React 101Neoito — React 101
Neoito — React 101
 
Build web apps with react js
Build web apps with react jsBuild web apps with react js
Build web apps with react js
 
React & The Art of Managing Complexity
React &  The Art of Managing ComplexityReact &  The Art of Managing Complexity
React & The Art of Managing Complexity
 
Getting Started with React v16
Getting Started with React v16Getting Started with React v16
Getting Started with React v16
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 

More from Luciano Mammino

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSLuciano Mammino
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoLuciano Mammino
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperLuciano Mammino
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Luciano Mammino
 
Everything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsEverything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsLuciano Mammino
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableLuciano Mammino
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Luciano Mammino
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinLuciano Mammino
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaLuciano Mammino
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)Luciano Mammino
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made SimpleLuciano Mammino
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessLuciano Mammino
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Luciano Mammino
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Luciano Mammino
 

More from Luciano Mammino (20)

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

Recently uploaded

TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTier1 app
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion Clinic
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfVictor Lopez
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAlluxio, Inc.
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessWSO2
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1KnowledgeSeed
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEJelle | Nordend
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?XfilesPro
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfmbmh111980
 
A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationHelp Desk Migration
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Soroosh Khodami
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Anthony Dahanne
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabbereGrabber
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)Max Lee
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring SoftwareMera Monitor
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Gáspár Nagy
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownloadvrstrong314
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Krakówbim.edu.pl
 

Recently uploaded (20)

TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data Migration
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring Software
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 

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...
  • 16.
  • 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
  • 20.
  • 21.
  • 22.
  • 23.
  • 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...
  • 107.
  • 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
  • 112.
  • 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