Slide 1

Slide 1 text

Universal JS Web Applications with React Luciano Mammino (@loige) Photo by Ryan Holloway on Unsplash loige.link/uni-js-workshop ROVINJ, Aug 30 2017

Slide 2

Slide 2 text

Who is Luciano? Principal Application Engineer Connect Website - Twitter - GitHub - Linkedin fullstackbulletin.com -15% Print (NpBK15WSCR) -20% eBook (NeBK20WSCR)

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

ISOMORPHIC UNIVERSAL… what? loige.link/universal-js-story

Slide 5

Slide 5 text

NOT ONLY FOR THE WEB... Desktop applications Mobile applications Hardware!

Slide 6

Slide 6 text

ADVANTAGES OF UNIVERSAL JAVASCRIPT "JavaScript-only" development Maintainability Better SEO Faster "perceived" load time

Slide 7

Slide 7 text

MOAR ADVANTAGES... Keep using React/JS paradigms for "static" websites Speed up content loading with linkprefetch loige.link/universal-react-made-easy-talk

Slide 8

Slide 8 text

In the wild

Slide 9

Slide 9 text

Seems cool… but...

Slide 10

Slide 10 text

MODULE SHARING Use Node.js modules in the browser

Slide 11

Slide 11 text

UNIVERSAL RENDERING Render the views of the application from the server (first request) and then in the browser (next requests)

Slide 12

Slide 12 text

UNIVERSAL ROUTING Recognise the view associated to the current route from both the server and the browser.

Slide 13

Slide 13 text

UNIVERSAL DATA RETRIEVAL Access data (and APIs) from both the server and the browser. AXIOS UNIVERSAL FETCH

Slide 14

Slide 14 text

UNIVERSAL STATE Manage changes on the state tree both on the server and the client...

Slide 15

Slide 15 text

ALTERNATIVE JS?!

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

OK… let's stop complaining and start coding!

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

WHAT ARE WE GOING TO BUILD? loige.link/judo-heroes-app ​ loige.link/judo-heroes-tutorial

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Quick Demo

Slide 25

Slide 25 text

WHAT TOOLS ARE WE GOING TO USE?

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

A React primer ● Components ● JSX ● Composition ● Props ● Events & State

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Online React Sandbox codesandbox.io

Slide 30

Slide 30 text

Pure function syntax import React from 'react'; import { render } from 'react-dom'; const IAmAComponent = () => (

This is a component as a pure function

); render(, document.getElementById('root'));

Slide 31

Slide 31 text

Class extend syntax import React from 'react'; import { render } from 'react-dom'; class IAmAnotherComponent extends React.Component { render() { return (

This is a component created with ES6 classes

) } } render(, document.getElementById('root'));

Slide 32

Slide 32 text

✏ 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

Slide 33

Slide 33 text

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 )

Slide 34

Slide 34 text

  • Mushrooms
  • Lasagna
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)

Slide 35

Slide 35 text

JSX examples import React from 'react'; import { render } from 'react-dom'; const SampleComponent = () => (

Last render time: {(new Date).toISOString()}

) render(, document.getElementById('root'));

Slide 36

Slide 36 text

React components tree ParentComponent FirstChildComponent SecondChildComponent

Slide 37

Slide 37 text

Components Tree import React from 'react'; import { render } from 'react-dom'; const FirstChildComponent = () => (

FirstChildComponent

) const SecondChildComponent = () => (

SecondChildComponent

) const ParentComponent = () => (

ParentComponent

) render(, document.getElementById('root'));

Slide 38

Slide 38 text

✏ 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

Slide 39

Slide 39 text

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) => (

Received props

{
JSON.stringify(props, null, 2)
}
) render(, document.getElementById('root'));

Slide 40

Slide 40 text

Reusable components import React from 'react'; import { render } from 'react-dom'; const ChildComponent = (props) => (

{props.name}

) const ParentComponent = () => (

ParentComponent

) render(, document.getElementById('root'));

Slide 41

Slide 41 text

✏ 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

Slide 42

Slide 42 text

Decorator components import React from 'react'; import { render } from 'react-dom'; const Center = (props) => (
{props.children}
) const HelloWorld = ({name = 'World'}) => (

Hello {name}

) const App = () => ( ) render(, document.getElementById('root'));

Slide 43

Slide 43 text

Iterations import React from 'react'; import { render } from 'react-dom'; const difficoultThings = [ 'Naming things', 'Cache invalidation', 'Off by one errors' ]; const App = () => (

The 2 most difficoult things in IT

    {difficoultThings.map((thing) => (
  1. {thing}
  2. ) )}
); render(, document.getElementById('root'));

Slide 44

Slide 44 text

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 (
No transaction available
) } return (

Latest transactions

    { data.map((transaction) => (
  • {transaction.amount} ({transaction.description})
  • )) }
) } const App = () => (

You have { money > 0 ? 'some': 'no' } Money!

Current Balance: {money}

); render(, document.getElementById('root'));

Slide 45

Slide 45 text

✏ 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

Slide 46

Slide 46 text

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 (
Click me!
) } } render(, document.getElementById('root'));

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Setup local dev environment mkdir judo-heroes-2 cd judo-heroes-2 npm init -y

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Folders structure mkdir -p \ src/components \ src/data \ src/views \ static <- React components <- Data file <- Server templates <- Static assets (CSS, images)

Slide 52

Slide 52 text

Babel Config .babelrc { "presets": ["react", "es2015"] }

Slide 53

Slide 53 text

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' }, }, ], }, };

Slide 54

Slide 54 text

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()] };

Slide 55

Slide 55 text

static/index.html (for development) Judo Heroes - A Universal JavaScript demo application with React

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Temporary src/app-client.js (just to test our local setup!) import React from 'react'; import { render } from 'react-dom'; const AppClient = () => (

Hello Rovinj

); window.onload = () => { render(, document.getElementById('main')); };

Slide 59

Slide 59 text

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!

Slide 60

Slide 60 text

dev env is finally ready!

Slide 61

Slide 61 text

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 ☕

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Flag component (src/components/Flag.js) import React from 'react'; export const Flag = props => ( {`${props.name}'s {props.showName && {props.name}} ); export default Flag; Props: ● name (e.g. "France") ● icon (e.g. "flag-fr.png") ● showName (true|false)

Slide 72

Slide 72 text

Medal component (src/components/Medal.js) import React from 'react'; export const medalTypes = { G: 'Gold', S: 'Silver', B: 'Bronze', }; export const Medal = props => (
  • {props.type} {props.year} {props.city} ({props.event}) {props.category}
  • ); 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")

    Slide 73

    Slide 73 text

    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 }) => ( {label} ); export const AthletesMenu = ({ athletes }) => ( { athletes.map(athlete => )} ); export default AthletesMenu; Props: ● athletes (our data object) import athletes from './data/athletes'

    Slide 74

    Slide 74 text

    AthleteCard component (src/components/AthleteCard.js) import React from 'react'; export const AthleteCard = props => (
    {`${props.name}'s

    {props.name}

    Medal icon {props.medals.length}
    ); export default AthleteCard; Props: id , image , name , medals (Attributes of an athlete in our data file) import athletes from './data/athletes' Spread syntax: Passes all the entries (key/values) of the athletes[0] object as props

    Slide 75

    Slide 75 text

    IndexPage component (src/components/IndexPage.js) import React from 'react'; import { AthleteCard } from './AthleteCard'; export const IndexPage = ({ athletes }) => (
    {athletes.map( athleteData => , )}
    ); export default IndexPage; Props: ● athletes (our data object) import athletes from './data/athletes'

    Slide 76

    Slide 76 text

    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 (
    {`${athlete.name}'s

    {athlete.name}

    Olympic medalist from  , born in {athlete.birth} (Find out more on Wikipedia).

    Winner of {athlete.medals.length} medals:

      { athlete.medals.map(medal => ) }
    ); }; export default AthletePage; Props: ● athletes (our data object) ● athlete (the selected athlete) import athletes from './data/athletes'

    Slide 77

    Slide 77 text

    NotFoundPage component (src/components/NotFoundPage.js) import React from 'react'; export const NotFoundPage = () => (

    404

    Page not found!

    Go back to the main page

    ); export default NotFoundPage; Props: -

    Slide 78

    Slide 78 text

    Layout component (src/components/Layout.js) import React from 'react'; export const Layout = props => (
    Judo Heroes logo
    {props.children}

    This is a demo app to showcase universal Javascript with React and Express.

    ); export default Layout; Props: ● children (the element to render as main content) Your content here...

    Slide 79

    Slide 79 text

    How do we assemble our components into a navigable prototype? We need routing!

    Slide 80

    Slide 80 text

    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

    Slide 81

    Slide 81 text

    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

    Slide 82

    Slide 82 text

    import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link } from 'react-router-dom' const Page = ({name}) => (

    {name}

    ) const Home = () => () const Page1 = () => () const Page2 = () => () const Menu = () => (
    • Home
    • Page1
    • Page2
    ) const App = () => (

    ) render(, 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

    Slide 83

    Slide 83 text

    import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link } from 'react-router-dom' const Page = ({name}) => (

    {name}

    ) const Menu = () => (
    • Home
    • Page1
    • Page2
    ) const App = () => (

    }/> }/> }/>
    ) render(, document.getElementById('root')); Alternative syntax with render prop render prop syntax

    Slide 84

    Slide 84 text

    import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom' const Page = ({name}) => (

    {name}

    ) const Menu = () => (
    • Home
    • Page1
    • Page2
    ) const App = () => (

    }/> }/> }/> }/>
    ) render(, document.getElementById('root')); Switch will render only the first route that match (or the last if none match) Switch and default route

    Slide 85

    Slide 85 text

    import React from 'react' import { render } from 'react-dom'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom' const Page = ({name}) => (

    {name}

    ) const Menu = () => (
    • Home
    • Page1
    • Page2
    ) const App = () => (

    }/> }/> }/>
    ) render(, 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.

    Slide 86

    Slide 86 text

    ✏ 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

    Slide 87

    Slide 87 text

    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

    Slide 88

    Slide 88 text

    Let's define our routes ● IndexPage: / ● AthletePage: /athlete/:id ● NotFoundPage: Everything else

    Slide 89

    Slide 89 text

    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 = () => ; const renderAthlete = ({ match, staticContext }) => { const id = match.params.id; const athlete = athletes.find(current => current.id === id); if (!athlete) { return ; } return ; }; export const App = () => ( ); export default App; Assemble client app src/components/App.js

    Slide 90

    Slide 90 text

    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 = () => ( ); window.onload = () => { render(, document.getElementById('main')); };

    Slide 91

    Slide 91 text

    Let's test it

    Slide 92

    Slide 92 text

    Clicking links makes the page refresh! We need to use the component! 16:20

    Slide 93

    Slide 93 text

    src/components/AthleteCard.js import React from 'react'; export const AthleteCard = props => ( ); export default AthleteCard; import React from 'react'; import { Link } from 'react-router-dom'; export const AthleteCard = props => ( … ); export default AthleteCard;

    Slide 94

    Slide 94 text

    src/components/AthletePage.js import React from 'react'; export const AthletePage = ({ athlete, athletes }) => { const headerStyle = { backgroundImage: `url(/img/${athlete.cover})` }; return ( … « Back to the index … ); }; 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 ( … « Back to the index … ); }; export default AthletePage;

    Slide 95

    Slide 95 text

    src/components/AthletesMenu.js import React from 'react'; … const AhtleteMenuLink = ({ to, label }) => ( {label} ); … export default AthletesMenu; import React from 'react'; import { Link } from 'react-router-dom'; … const AhtleteMenuLink = ({ to, label }) => ( {label} ); … export default AthletesMenu;

    Slide 96

    Slide 96 text

    src/components/Layout.js import React from 'react'; export const Layout = props => (
    Judo Heroes logo
    ); export default Layout; import React from 'react'; import { Link } from 'react-router-dom'; export const Layout = props => (
    Judo Heroes logo
    ); export default Layout;

    Slide 97

    Slide 97 text

    src/components/NotFoundPage.js import React from 'react'; export const NotFoundPage = () => (

    404

    Page not found!

    Go back to the main page

    ); export default NotFoundPage; import React from 'react'; import { Link } from 'react-router-dom'; export const NotFoundPage = () => (

    404

    Page not found!

    Go back to the main page

    ); export default NotFoundPage;

    Slide 98

    Slide 98 text

    Now everything should be fine! 16:30

    Slide 99

    Slide 99 text

    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 }) => ( {label} ); … export default AthletesMenu; import React from 'react'; import { Link, Route } from 'react-router-dom'; … const AhtleteMenuLink = ({ to, label }) => ( {({ match }) => ( {label} )} ); … 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!

    Slide 100

    Slide 100 text

    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

    Slide 101

    Slide 101 text

    React Server Side Rendering (SSR) // src/testSSR.js import React from 'react'; import { renderToString } from 'react-dom/server'; const SampleApp = () => (

    Hello World

    ); console.log(renderToString()); node_modules/.bin/babel-node src/testSSR.js

    Slide 102

    Slide 102 text

    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 = () => ( ); console.log(renderToString()); StaticRouter is an implementation of React Router that accepts the location path as a prop.

    Slide 103

    Slide 103 text

    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 ) Judo Heroes - A Universal JavaScript demo application with React
    <%- markup -%>
    This placeholder will be replaced with the markup rendered with React on the server side

    Slide 104

    Slide 104 text

    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( , ); 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

    Slide 105

    Slide 105 text

    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' }, }, ], }, };

    Slide 106

    Slide 106 text

    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...

    Slide 107

    Slide 107 text

    No content

    Slide 108

    Slide 108 text

    "BAD JUJU" for SEO!

    Slide 109

    Slide 109 text

    How to report proper 404 status from the server? By using the rendering context! 16:55

    Slide 110

    Slide 110 text

    src/components/NotFoundPage.js import React from 'react'; import { Link } from 'react-router-dom'; export const NotFoundPage = () => (

    404

    Page not found!

    Go back to the main page

    ); 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 (

    404

    Page not found!

    Go back to the main page

    ); } } export default NotFoundPage; staticContext is available when rendering from StaticRouter and allows components to exchange arbitrary data will rendering

    Slide 111

    Slide 111 text

    src/server.js // ... // universal routing and rendering app.get('*', (req, res) => { let status = 200; const context = {}; const markup = renderToString( , ); 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

    Slide 112

    Slide 112 text

    No content

    Slide 113

    Slide 113 text

    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

    Slide 114

    Slide 114 text

    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

    Slide 115

    Slide 115 text

    "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' }, }], }, };

    Slide 116

    Slide 116 text

    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

    Slide 117

    Slide 117 text

    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

    Slide 118

    Slide 118 text

    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

    Slide 119

    Slide 119 text

    Huge thanks to @andreaman87 and @quasi_modal (Follow them on Twitter!) FEEDBACK TIME https://joind.in/talk/85338