60
Rome 24-25 MARCH 2017 { Universal JS Web Applications with React Luciano Mammino loige.link/codemotion-rome-2017 1

Universal JavaScript Web Applications with React - Luciano Mammino - Codemotion Rome 2017

Embed Size (px)

Citation preview

Rome 24-25 MARCH 2017

{ Universal JS Web Applications with React

Luciano Mammino

loige.link/codemotion-rome-20171

AGENDA

1. The term "Universal" JS

2. Who & Why

3. Common problems and technologies

4. Building a frontend only Single Page App

5. Making it universal

5

ISOMORPHIC

loige.link/universal-js-story

UNIVERSAL WHAT?6

NOT ONLYFOR THE WEB...

Desktop applications

Mobile applications

Hardware!

7

ADVANTAGESOF UNIVERSAL JAVASCRIPT

"JavaScript-only" development

Maintainability

Better SEO

Faster "perceived" load time8

ADVANTAGES...MORE

Keep using React/JS paradigms also to

generate "static" websites

Speed up content loading with

linkprefetch

loige.link/universal-react-made-easy-talk9

IN THE WILD

10

IT LOOKS GREAT BUT...

11

UNIVERSAL RENDERING

Render the views of the application from

the server (first request) and then in the

browser (next requests)

13

UNIVERSAL ROUTING

Recognise the view associated to the

current route from both the server and the

browser.

14

UNIVERSAL DATA RETRIEVAL

Access data (and APIs) from both the server

and the browser.

AXIOS UNIVERSALFETCH

15

UNIVERSAL STATEMANAGEMENT

Manage changes on the state tree both on

the server and the client...

16

FUTURISTIC/ALTERNATIVE JS?!

17

18

OK...LET'S STOP COMPLAININGAND BUILD SOMETHING!

19

WHAT ARE WEGOING TO BUILD?

loige.link/judo-heroes-app

loige.link/judo-heroes-tutorial

v 2.0

20

21

22

23

curl -sS "https://judo-heroes.herokuapp.com/athlete/teddy-riner"

24

27

The data set// src/data/athletes.js

const athletes = [ { id: 'driulis-gonzalez', name: 'Driulis González', country: { id: 'cu', name: 'Cuba', icon: 'flag-cu.png', }, birth: '1973', image: 'driulis-gonzalez.jpg', cover: 'driulis-gonzalez-cover.jpg', link: 'https://en.wikipedia.org/wiki/Driulis_González', medals: [ { id: 1, year: '1992', type: 'B', city: 'Barcelona', event: 'Olympic Games', category: { id: 2, year: '1993', type: 'B', city: 'Hamilton', event: 'World Championships', category: { id: 3, year: '1995', type: 'G', city: 'Chiba', event: 'World Championships', category: { id: 4, year: '1995', type: 'G', city: 'Mar del Plata', event: 'Pan American Games', { id: 5, year: '1996', type: 'G', city: 'Atlanta', event: 'Olympic Games', category: // ... ], }, // ...];

export default athletes;

28

REACTCOMPONENTS

29

Layout component

30

IndexPage component

31

AthletePage component

32

NotFoundPage component

33

AthletePreview component

34

AthletesMenu component

35

Flag component

36

Medal component

37

// src/components/Layout.js

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" /> </Link> </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;

38

// src/components/IndexPage.js

import React from 'react';import { AthletePreview } from './AthletePreview';

export const IndexPage = ({ athletes }) => ( <div className="home"> <div className="athletes-selector"> { athletes.map( athleteData => <AthletePreview key={athleteData.id} {...athleteData} /> ) } </div> </div>);

export default IndexPage;39

// src/components/AthletePreview.js

import React from 'react';import { Link } from 'react-router';

export const AthletePreview = (props) => ( <Link to={`/athlete/${props.id}`}> <div className="athlete-preview"> <img src={`img/${props.image}`}/> <h2 className="name">{props.name}</h2> <span className="medals-count"> <img src="/img/medal.png"/> {props.medals.length} </span> </div> </Link>);

export default AthletePreview;

40

ROUTING

41

2 ROUTES

Index Page: /

Athlete Page: /athlete/:id

42

// src/components/App.js

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';

// ...

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;

43

// src/components/App.js

// ...

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} />;};

44

CLIENT APP

45

// 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') );};

46

HTML TEMPLATE

47

// 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="/js/bundle.js"></script> </body></html>

48

BUILD CONFIG(BABEL + WEBPACK)

49

.babelrc

import path from 'path';

const config = { entry: { js: './src/app-client.js', }, output: { path: path.join(__dirname, 'src', 'static', 'js'), filename: 'bundle.js', }, module: { rules: [ { test: path.join(__dirname, 'src'), use: { loader: 'babel-loader' }, }, ], },};

.webpack.config.babel.js

{ "presets": ["react", "es2015"]}

50

LET'S BUILD IT!

51

// src/server.js

import path from 'path';import { Server } from 'http';import Express from 'express';

const app = new Express();const server = new Server(app);

// use ejs templatesapp.set('view engine', 'ejs');app.set('views', path.join(__dirname, 'views'));

// define the folder that will be used for static assetsapp.use(Express.static(path.join(__dirname, 'static')));

// render the index for every non-matched routeapp.get('*', (req, res) => { let markup = ''; let status = 200; return res.status(status).render('index', { markup });});

// start the serverconst port = process.env.PORT || 3000;const env = process.env.NODE_ENV || 'production';server.listen(port);

"Static" Express server

52

READY... LET'S TEST IT

53

RECAPWhat we learned so far

1. Define views combining React components

2. Add routing using React Router

3. Compile the client bundle with Babel and

Webpack

4. Run the app with a static Express server54

SERVER SIDERENDERING AND

ROUTING

55

Updating the server app// ...import { renderToString } from 'react-dom/server';import { StaticRouter as Router } from 'react-router-dom';import { App } from './components/App';

// ...app.get('*', (req, res) => { let markup = ''; let status = 200;

const context = {}; markup = renderToString( <Router location={req.url} context={context}> <App /> </Router>, );

// context.url will contain the URL to // redirect to if a <Redirect> was used if (context.url) { return res.redirect(302, context.url); }

if (context.is404) { status = 404; }

return res.status(status).render('index', { markup });});

56

THAT'S IT!

LET'S TEST AGAIN

57

RECAPWhat we learned so far

1. Create a Single Page Application with

React and React Router

2. Add server side routing and rendering

using React and React Router libraries in the

Express app58

UNIVERSAL DATA RETRIEVAL

api-proxy & async-props(COMPLETE CHAPTER in )

UNIVERSAL STATE MANAGEMENT

Redux

Node.js Design Patterns

WHERE DO WE GOfrom here...

Code: loige.link/judo-heroes-2 59