workshop 27: isomorphic web apps with reactjs

Post on 23-Jan-2018

191 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Front End WorkshopsIsomorphic Web Apps

With ReactJs

Marc Torrent

mtorrent@visual-engin.com

Isomorphic what??

First review ReactJS + Redux recap workshops

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.

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

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>

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

Server Side Rendering: server.js - Main Entry Point

Server Side Rendering: server.js - HTML Template

Client main entry point: client.js

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

Avoiding FOUC - Webpack ExtractTextPlugin

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

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

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

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

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

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

REDIAL → Data fetching before rendering

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

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

React Router + Redux + Redial: provideHooks

FAQ-Component.js

FAQ-Actions.js

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

Bonus Track

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

React-Helmet - Server Side Rendering

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

Thank you!

top related