Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Universal JavaScript - Frontend United Athens 2017

Universal JavaScript - Frontend United Athens 2017

Since we started to see JS on the server side, the dream of developers has been to reduce the gap and the cost of switch between frontend/backend. Today with Node.js, React and a whole ecosystem of tools, this dream is becoming true! In this talk, I am going to discuss Universal (a.k.a. Isomorphic) JS and present some practical example regarding the major patterns related to routing, data retrieval and rendering. I will use Node, React, Webpack, Babel and React Router and give you a series of example to get you started easily with this new technology trend.

Luciano Mammino

May 29, 2017
Tweet

More Decks by Luciano Mammino

Other Decks in Technology

Transcript

  1. NeBK20FUA -20% eBook NpBK15FUA -15% Print loige.link/node-book ⚡ ⚡CONTAINS A

    WHOLE CHAPTER ABOUT UNIVERSAL JAVASCRIPT ⚡ ⚡ GET A DISCOUNT ​ 3
  2. 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" 6
  3. ADVANTAGES... MOAR Keep using React/JS paradigms also for "static" websites

    Speed up content loading with linkprefetch loige.link/universal-react-made-easy-talk 10
  4. UNIVERSAL RENDERING Render the views of the application from the

    server (first request) and then in the browser (next requests) 14
  5. UNIVERSAL DATA RETRIEVAL Access data (and APIs) from both the

    server and the browser. AXIOS UNIVERSAL FETCH 16
  6. 19

  7. 22

  8. 23

  9. 24

  10. 28

  11. 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: '-57kg' }, { id: 2, year: '1993', type: 'B', city: 'Hamilton', event: 'World Championships', category: '-57kg' }, { id: 3, year: '1995', type: 'G', city: 'Chiba', event: 'World Championships', category: '-57kg' }, { id: 4, year: '1995', type: 'G', city: 'Mar del Plata', event: 'Pan American Games', category: '-57kg' }, { id: 5, year: '1996', type: 'G', city: 'Atlanta', event: 'Olympic Games', category: '-57kg' }, // ... ], }, // ... ]; export default athletes; 29
  12. // 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; 39
  13. // 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; 40
  14. // 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; 41
  15. // 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; 44
  16. // 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} />; }; 45
  17. // 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') ); }; 47
  18. // 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> 49
  19. .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' }, }, ], }, }; export default config; .webpack.config.babel.js { "presets": ["react", "es2015"] } 51
  20. // 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 templates app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // define the folder that will be used for static assets app.use(Express.static(path.join(__dirname, 'static'))); // render the index for every non-matched route app.get('*', (req, res) => { let markup = ''; let status = 200; return res.status(status).render('index', { markup }); }); // start the server const port = process.env.PORT || 3000; const env = process.env.NODE_ENV || 'production'; server.listen(port); "Static" Express server 53
  21. RECAP What 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 server 55
  22. 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 }); }); 57
  23. RECAP What 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 app 59
  24. UNIVERSAL DATA RETRIEVAL api-proxy & async-props (COMPLETE CHAPTER in )

    UNIVERSAL STATE MANAGEMENT Redux PROGRESSIVE WEB APPS (PWA) + Node.js Design Patterns @addyosmani's tutorial Create React App WHERE DO WE GO from here... Code: loige.link/judo-heroes-2 60
  25. THANKS! loige loige.co lmammino (Special thanks to , , Aleksandar

    Čambas & ) @cirpo @andreaman87 @quasi_modal loige.link/athens-2017​ 61