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

Universal JS Web Applications with React - Luci...

Universal JS Web Applications with React - Luciano Mammino - Codemotion Rome 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 technologies. Today with Node.js, React and a whole ecosystem of tools, this dream is becoming real! In this talk, I am going to discuss Universal (a.k.a. Isomorphic) JavaScript and present some practical example regarding the significant 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.

(interactive slides at http://slides.com/lucianomammino/universal-js-web-applications-with-react-codemotion-rome-2017 )

Luciano Mammino

March 26, 2017
Tweet

More Decks by Luciano Mammino

Other Decks in Technology

Transcript

  1. Rome 24-25 MARCH 2017 { Universal JS Web Applications with

    React Luciano Mammino loige.link/codemotion-rome-2017 1
  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 5
  3. ADVANTAGES... MORE Keep using React/JS paradigms also to generate "static"

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

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

    server and the browser. AXIOS UNIVERSAL FETCH 15
  6. 18

  7. 21

  8. 22

  9. 23

  10. 27

  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 { id: 2, year: '1993', type: 'B', city: 'Hamilton', event: 'World Championships', cat { id: 3, year: '1995', type: 'G', city: 'Chiba', event: 'World Championships', catego { 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
  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; 38
  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; 39
  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; 40
  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; 43
  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} />; }; 44
  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') ); }; 46
  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> 48
  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' }, }, ], }, }; .webpack.config.babel.js { "presets": ["react", "es2015"] } 50
  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 52
  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 54
  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 }); }); 56
  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 58
  24. UNIVERSAL DATA RETRIEVAL api-proxy & async-props (COMPLETE CHAPTER in )

    UNIVERSAL STATE MANAGEMENT Redux Node.js Design Patterns WHERE DO WE GO from here... Code: loige.link/judo-heroes-2 59
  25. THANKS! loige loige.co lmammino (Special thanks to , , Aleksandar

    Čambas & ) @cirpo @andreaman87 @quasi_modal loige.link/codemotion-rome-2017 60