26
Front End Workshops Isomorphic Web Apps With ReactJs Marc Torrent [email protected]

Workshop 27: Isomorphic web apps with ReactJS

Embed Size (px)

Citation preview

Page 1: Workshop 27: Isomorphic web apps with ReactJS

Front End WorkshopsIsomorphic Web Apps

With ReactJs

Marc Torrent

[email protected]

Page 2: Workshop 27: Isomorphic web apps with ReactJS

Isomorphic what??

First review ReactJS + Redux recap workshops

Page 3: Workshop 27: Isomorphic web apps with ReactJS

Universal web apps - Isomorphic

Web Application Code (JS)

Server - Routing and Data gathering (Rest API)

ClientBrowser

HTTP Request,a new Page

With the data, inject it to our application code

render() and obtainvalid HTML

HTTP Response with HTML, Web Application Code and JSON Data

Web Application Code (JS)

Request Data, AJAX or Web sockets → JSON Routing

Isomorphic: the Client and the Server share the same code for routing, data gathering and rendering.

Page 4: Workshop 27: Isomorphic web apps with ReactJS

HTML Page

Server Side Rendering (SSR) with ReactJS

react-dom/server renderToString(ReactElement)

<MyReactComponent {...props} />

HTML + ReactJS Virtual DOM ID’sReactDOM.render(App(window.APP_PROPS),

document.getElementById('content')

No re-rendering as there’s no difference in Virtual DOM !!!

Page 5: Workshop 27: Isomorphic web apps with ReactJS

Server Side Rendering with Redux

react-dom/server const htmlEl = renderToString(ReactElement)

const store = createStore(reducers);

<Provider store={store}> <MyReactComponent {...props} /></ Provider>

No re-rendering as there’s no difference in Virtual DOM !!!

const initialState = store.getState();

const html = `<HTML> <body>

<script>window.initialState = JSON.Stringify(initialState);</script>

<div>${htmlEl}</div></body></HTML>`;

res.send(html);

Client.jsx

const initialState = window.initialState;const store = createStore(reducers, initialState);

<Provider store={store}> <MyReactComponent {...props} /></ Provider>

Page 6: Workshop 27: Isomorphic web apps with ReactJS

Server Side Rendering with React Router

import { createMemoryHistory, RouterContext, match } from 'react-router';

Finds the route from the current location and returns the component to be rendered.

Creates a Location object from the current url to be used by the match function.

Renders the component tree for a given router state.

import { createMemoryHistory, RouterContext, match } from 'react-router';

const history = createMemoryHistory(req.path);

match({ routes, history }, (err, redirectLocation, renderProps) => {…const InitialComponent = (<RouterContext {...renderProps} />);…const componentHTML = renderToString(InitialComponent);...

Page 7: Workshop 27: Isomorphic web apps with ReactJS

Server Side Rendering: server.js - Main Entry Point

Page 8: Workshop 27: Isomorphic web apps with ReactJS

Server Side Rendering: server.js - HTML Template

Page 9: Workshop 27: Isomorphic web apps with ReactJS

Client main entry point: client.js

Page 10: Workshop 27: Isomorphic web apps with ReactJS

Webpack bundles

Entry point Output

Loaders

main: [ ‘client.js’},vendor: { ‘react’, ‘react-dom’, ‘react-router’, ‘redux’}

.../main.js

.../vendor.js

.../[chunk_id].chunk.js

Babel_loader: ES6 → ES5

Style_loader | css_loader | sass_loader Plugins

HotModuleReplacementPlugin()CommonsChunkPlugin()DefinePlugin()ProvidePlugin()ExtractTextPlugin()

Module

Page 11: Workshop 27: Isomorphic web apps with ReactJS

Avoiding FOUC - Webpack ExtractTextPlugin

Page 12: Workshop 27: Isomorphic web apps with ReactJS

Webpack code splitting

// polyfill webpack require.ensureif (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require);

require.ensure([‘myModulesDependencies’], (require) => {const a = require('./ModuleA').default;const b = require('./ModuleB').default;

});

Output.../main.js

.../vendor.js

.../1.chunk.js

.../2.chunk.js

Page 13: Workshop 27: Isomorphic web apps with ReactJS

React Router - Configuration with Plain Routes

<Route path=”/” component={App}> <IndexRoute component={Dashboard} /> <Route path=”about” component={About} /> <Route path=”inbox” component={Inbox} > <Route path=”messages/:id” component={Message} /> </Route></Route>

{ path: ‘/’, component: App, indexRoute: { component: Dashboard }, childRoutes: [ { path: ‘about’, component: About }, { path: ‘inbox’, component: Inbox, childRoutes: [ { path: ‘messages/:id’, component: Message} ] } ]}

from ...

… to

Page 14: Workshop 27: Isomorphic web apps with ReactJS

React Router - Dynamic Routing & WebPack

{ path: ‘/’, component: App, indexRoute: { component: Dashboard },

getChildRoutes(location, cb) {require.ensure([], require => {

cb(null, [require(‘./About’).default,require(‘./Inbox’).default

]);});

}}

import About from ‘./components/About’;

{ path: ‘about’, component: About}

{ path: ‘inbox’, component: Inbox,

childRoutes: [{ path: ‘messages/:id’, component: Message}

]}

About.js Inbox.js

Page 15: Workshop 27: Isomorphic web apps with ReactJS

Dynamic Routing with new Reducers

At initialization we combine reducers to build the store...

const rootReducer = combineReducers({authReducer, otherReducer});

const store = createStore(rootReduer, initialState, applyMiddleware(...middleware);

…<Provider store={store}>

<Router routes={routes} /></Proider>…

But what happens if we load a route containing a new reducer that is needed for the components of that new

route???

Page 16: Workshop 27: Isomorphic web apps with ReactJS

Combining new Reducers - ReducerRegistry

{ path: ‘faq’,

getComponents:(location, cb) {require.ensure([‘./components/FAQ’, ‘./reducer’], require => {

const FAQ = require(‘./components/FAQ’).default;const faqReducer = require(‘./reducer’).default;store.replaceReducer(combineReducers({...existingRedu

cers, faqReducer}));

cb(null, FAQ);});

}}

Now we have the initial reducers combined with the new ones and applied to the store via store.replaceReducer

Page 17: Workshop 27: Isomorphic web apps with ReactJS

Data fetching before rendering

We need data to be accessible to Route Components before rendering those components.

In SSR it’s not possible to fetch data in componentWillMount or componentDidMount:1. componentDidMount is not available on server rendering.2. componentWillMount is called immediately before rendering and changes

in state won’t trigger a re-rendering → fetching data here doesn’t ensures that the render method will have all the data that is needed.

ReactRouter → onEnter = Function to be called before rendering a Route’s Component

Page 18: Workshop 27: Isomorphic web apps with ReactJS

REDIAL → Data fetching before rendering

HOC exposing three actions to take BEFORE & AFTER rendering & only CLIENT SIDE.

Page 19: Workshop 27: Isomorphic web apps with ReactJS

React Router + Redux + Redial: Server Side

1. Call trigger function which is an action creator that returns a Promise

2. State is update through usual Redux cycle3. Render the vurrent Route with the current state4. Get the state and make it available to the client

Page 20: Workshop 27: Isomorphic web apps with ReactJS

React Router + Redux + Redial: provideHooks

FAQ-Component.js

FAQ-Actions.js

Page 21: Workshop 27: Isomorphic web apps with ReactJS

React Router + Redux + Redial: Client Side

1. Listen to navigation events: browserHistory.listen()2. Match routes to get the component3. Trigger fetch on the component4. Render the Component

Page 22: Workshop 27: Isomorphic web apps with ReactJS

Bonus Track

Page 23: Workshop 27: Isomorphic web apps with ReactJS

SEO friendly universal web apps - React-Helmet

Isomorphic Web Apps is intended to improve SEO….... we need something to manage our meta tags !!!!

React Helmet is the solution for managing the meta tags of a Route Component

Page 24: Workshop 27: Isomorphic web apps with ReactJS

React-Helmet - Server Side Rendering

const head = Helmet.rewind(); head.title.toString()

Page 25: Workshop 27: Isomorphic web apps with ReactJS

Thank you!

Page 26: Workshop 27: Isomorphic web apps with ReactJS