"Surprisingly, the proportion of people that have
explicitly disabled JavaScript or use a browser
that doesn't support JavaScript, only makes up a
small slice of people that don't run JavaScript."
Slide 11
Slide 11 text
"Progressive enhancement has never been about
users who've turned JavaScript off, or least it
wasn't for me."
Jake Archibald, "Progressive Enhancement Still
Important"
Slide 12
Slide 12 text
Everyone has JS, right?
• On a train / in a tunnel / etc
• HTTP request hangs
• Firewalls
• ISP is interfering
• A browser addon is messing with you
• Your CDN is down
Stuart Langridge's Flowchart
Slide 13
Slide 13 text
Think about if
this is right for
you!
Slide 14
Slide 14 text
Cutting Edge!
We're really still at the early stages of figuring
out how this stuff works.
Some of the code shown here isn't the easiest, or
the APIs aren't that straight forward.
This will change as we learn more. Don't expect
this to be 100% smooth!
Slide 15
Slide 15 text
React paved the
way
Slide 16
Slide 16 text
No content
Slide 17
Slide 17 text
No content
Slide 18
Slide 18 text
Code and demos: https://github.com/jackfranklin/
universal-react-talk/tree/fluent-conf-version
Slide 19
Slide 19 text
A standard React app:
class MyApp extends React.Component {
render() { ... }
}
ReactDOM.render(
,
document.getElementById('app')
)
Slide 20
Slide 20 text
Server side:
class MyApp extends React.Component {
render() { ... }
}
ReactDOM.renderToString()
Slide 21
Slide 21 text
// some imports left out to save space
import React from 'react';
import MyApp from './component';
import { renderToString } from 'react-dom/server';
const app = express();
app.get('*', (req, res) => {
const markup = renderToString();
res.render('index', { markup });
});
Slide 22
Slide 22 text
renderToString
When your HTML is going to be picked up by React
on the client
renderToStaticMarkup
When your HTML is never going to be edited by
client-side React
Slide 23
Slide 23 text
My App
Hello from React
Slide 24
Slide 24 text
Going Client side
• Shared set of components that are environment
agnostic
• A server rendering step (like we just saw)
• A client rendering step
• A bundler to generate our client side
JavaScript
Slide 25
Slide 25 text
There's no requirement to
actually go client side
A server side generated React app with no client
side JS is perfectly fine.
Slide 26
Slide 26 text
Agnostic
Components
Slide 27
Slide 27 text
Webpack
A JavaScript application bundler that will
generate our client side build.
Slide 28
Slide 28 text
Rendering on the
Client
Slide 29
Slide 29 text
Updating our server template.
<%- markup %>
Slide 30
Slide 30 text
Creating client.js:
import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './component';
ReactDOM.render(
,
document.getElementById('app')
);
A read-only
experience is way
better than no
experience.
Slide 39
Slide 39 text
Routing
Slide 40
Slide 40 text
Don't be that
person who breaks
the web.
Slide 41
Slide 41 text
react-router
The defacto, practically standard routing solution
for React.
https://github.com/rackt/react-router
Slide 42
Slide 42 text
First we need some more components, starting with
components/app.js:
import React from 'react';
export default class AppComponent extends React.Component {
render() {
return (
My web 2.0 app
{ this.props.children }
);
}
}
this.props.children are the nested routes.
Slide 43
Slide 43 text
And then components/index.js:
import React from 'react';
export default class IndexComponent extends React.Component {
render() {
return (
This is the index page
);
}
}
Slide 44
Slide 44 text
Define our routes:
import { Route } from 'react-router';
import React from 'react';
import AppComponent from './components/app';
import IndexComponent from './components/index';
export const routes = (
);
Slide 45
Slide 45 text
Match against the URL on the server.
Gets a bit hairy, stick with me!
React Router server side guide
Slide 46
Slide 46 text
// our newly defined routes
import { routes } from './routes';
// match is responsible for matching routes against a URL
// RouterContext renders the components in the matched routes
import { match, RouterContext } from 'react-router';
// take our app's routes, and the URL of the request
match({ routes, location: req.url },
(error, redirectLocation, renderProps) => {
// error: given if something went wrong matching a route
// redirectLocation: returned if the URL matches a redirect
// renderProps: given if a route was matched and we can render
...
});
Slide 50
Slide 50 text
if (error) {
// if there was an error, 500 with the error message
// you might show a custom error HTML page here
res.status(500).send(error.message)
} else if (redirectLocation) {
...
}
Slide 51
Slide 51 text
...
} else if (redirectLocation) {
// if we need to redirect, redirect to the new URL
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
...
}
Slide 52
Slide 52 text
...
} else if (renderProps) {
// if we have renderProps that means we have a match and can render
res.render('index', {
// RouterContext is React Router's wrapper around our app
// and renderProps contains all the info
// React Router needs to render our app
markup: renderToString()
});
} else {
...
}
Slide 53
Slide 53 text
} else {
// if we get here, it's not an error, redirect or match
// hence, 404!
res.status(404).send('Not found')
}
Slide 54
Slide 54 text
No content
Slide 55
Slide 55 text
Let's add an about page!
Slide 56
Slide 56 text
components/about.js:
import React from 'react';
export default class AboutComponent extends React.Component {
render() {
return
Rockstar developer
;
}
}
Slide 57
Slide 57 text
routes.js:
import AppComponent from './components/app';
import IndexComponent from './components/index';
import AboutComponent from './components/about';
import React from 'react';
import { Route } from 'react-router';
export const routes = (
);
Slide 58
Slide 58 text
And some links...
...
render() {
return (
My web 2.0 app
Home
About
{ this.props.children }
);
}
...
Slide 59
Slide 59 text
With no client side bundle, this works perfectly:
Slide 60
Slide 60 text
Updating the client side:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import { routes } from './routes';
ReactDOM.render(
,
document.getElementById('app')
)
And then rerun webpack.
Slide 61
Slide 61 text
No content
Slide 62
Slide 62 text
Dealing with Data
on the server and
client
Slide 63
Slide 63 text
(Caveat: this area is still
WIP)
Lots of unknowns!
Slide 64
Slide 64 text
1.We want to be able to fetch data on the server
and/or on the client.
2.If the data is loaded on the server and
rendered to the client, we ideally want to
avoid making the request again.
Slide 65
Slide 65 text
Async Props (also by the creator's of React
Router).
Not production ready, still a WIP. Check code/
with-async-data on GitHub for an example.
Slide 66
Slide 66 text
First, let's give components/index.js some data:
export default class IndexComponent extends React.Component {
// a stage 1 proposal for ES.next
static loadProps(params, cb) {
fetchGithubData('jackfranklin').then((data) => {
cb(null, { github: data });
});
}
render() {
return (
My github repo count: { this.props.github.public_repos }
);
}
}
Slide 67
Slide 67 text
Then we update our server and client rendering.
AsyncProps generates a script that will contain
the fetched data from the server.
The client can pick this data up and avoid having
to make the request all over again.
Slide 68
Slide 68 text
This is one of
many approaches.
Slide 69
Slide 69 text
React Resolver
code/with-react-resolver has an example.
class IndexComponent extends React.Component {
render() {
return (
This is the index page
My github repo count: { this.props.github.public_repos }
• Update the server side rendering to include
window.__REACT_RESOLVER_PAYLOAD__.
• Update the client side rendering to use React
Resolver.
• All data is resolved on the server, rendered
and then used to populate data on the client.
Slide 71
Slide 71 text
No content
Slide 72
Slide 72 text
This area is still being figured out - more
solutions will definitely come!
Slide 73
Slide 73 text
Code and Demos
These slides and all the demos are on GitHub.
jackfranklin/universal-react-talk
Please send me questions: @Jack_Franklin or
[email protected].
Slide 74
Slide 74 text
Universal JavaScript is here
to stay.
The techniques, libraries and approaches will
change over time.
There's plenty to figure out in this space!
Long term I expect frameworks to do more, and it
become even easier for developers to take
advantage.
Slide 75
Slide 75 text
Server side by
default?
Slide 76
Slide 76 text
Further Reading
• Universal React on 24ways
• How we built the new gocardless.com
• GoCardless.com repo
• Universal React Example app
Slide 77
Slide 77 text
If you dig React
I can make you dig it more! Day long React
workshops in London:
• 16 March
• 10 June