Isomorphic React

A whirlwind tour

18 April, 2017

What is this?

Why?

  • People like client side apps - once the app is loaded it *feels responsive*, no need to refresh
  • SEO, crawlers - not all can properly index client side apps
  • Users dont have to wait for all the assets to load to start using the app
  • Shared codebase for front and back

Considerations

  • Rendering the view
  • Routing / State
  • Populating the head tag
  • Styling components

View - rendering

`ReactDOM.renderToString` will render a react element to its initial HTML

This returns a HTML string and should only be used on the server


import App from 'Shared/app'

const appRender = (location, plainPartialState, routerContext = {}) => {
	// Create store + redux provider
	...
	const appHtml = ReactDOMServer.renderToString(<App />)
	return (
		
	)
}

						

appRender.jsx


<html>
	<head>
		${head.title}
		${head.meta}
		<link rel='stylesheet' href='${assetPath}/css/styles.css' />
	</head>
	<body>
		<div class="${APP_MOUNT_CONTAINER}">${appHtml}</div>
		<script src="${assetPath}/js/bundle.js"></script>
	</body>
</html>

						

HTML to render

Routing

  • Routing needs to be handled both on the client and the server
  • StaticRouter - a stateless router that doesnt change location
  • BrowserRouter - a router uses HTML5 history to keep the UI in sync with the url

Server

	
import App from 'Shared/app'

// location: the url the server received
// context: properties a component can add to store information
//          passed to the component via the staticContext prop
ReactDOM.renderToString(
	<StaticRouter location={} context={}>
		<App />
	</StaticRouter>
);
	

Client

	
import App from 'Shared/app'

ReactDOM.render(
	<BrowserRouter>
		<App />
	</BrowserRouter>
);
	

Initial state - server

On the server, we populate `window.__PRELOADED_STATE__`

	
<script>
	window.__PRELOADED_STATE__ = ${JSON.stringify(store.getState())}
</script>
	

appRender.html

Initial state - client

On the server, we populate `window.__PRELOADED_STATE__`

	
// Grab our preloaded state that came from the server
const preloadedState = window.__PRELOADED_STATE__

// create our store using the preloaded state
const store = createStore(
	combineReducers({ someStateSlice: someStateSliceReducer }),
	{ someStateSlice: preloadedState.someStateSlice }
);
	

index.jsx

Populating HEAD tags - React Helmet

  • Manually manipulates your HEAD tag to reflect updated page details
  • Allows us to manipulate tags such as style, link, meta, title etc. from within our components

Server

	
// grab parameters for our html head from the helmet component
const head = Helmet.rewind()
....
	<head>
		${head.title}
		${head.meta}
	</head>
....
	

Client

	
// TODO: move all this helmet shit to a HoC
import Helmet from 'react-helmet'
....
const title = 'Artist page'
const ArtistPage = () =>
  <div>
    <Helmet
      title={title}
      meta={[ { name: 'description', content: 'A tiny app' }, { property: 'og:title', content: title } ]}
    />
...
  </div>

export default ArtistPage

	

Styling

😅

  • Inline Styles
  • Isomorphic style loader - extracts critical styles needed for rendering from the server
  • CSS Modules - modular approach that creates globally unique styles
    • Conditional styling?
    • css-modules-require-hook
  • JSS - styles written in JS, that are injected into your app
    • 🤢

CSS modules

	
import React from 'react'
import styles from './ComponentStyles.css'
export default class StyledComponent extends React.Component {
...
  render() {
    return (
      <header>
        <h1 className={styles.someStyle}>🤙🏾🔥Yeaaaaah boiiiii</h1>
      </header>
    )
  }
}
	

CSS Modules - Conditional styling

							
if ( process.env.BROWSER ) {
    require('./StyledComponent.css');
}

export default class SomeComponent extends React.Component {
    render() {
        return (
            
{🤙🏾🤙🏾🤙🏾}
); } }

CSS Modules require hook

const hook = require('css-modules-require-hook');
hook({
  generateScopedName: '[name]_[local]__[hash:base64:5]',
  rootDir: projectDir
});
	

Reality...

  • Universal redux
  • Isomorphic.net - resources on isomorphic apps
  • Airbnb - isomorphic javascript overview - https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc

Useful Links

  • React-Helmet - https://github.com/nfl/react-helmet
  • Airbnb: isomorphic-javascript-the-future-of-web-apps - https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc
  • JSS: CSS in JS - https://github.com/cssinjs/jss
  • CXS: another approach to css in js: https://github.com/jxnblk/cxs
  • A web application journey: https://medium.com/@LopezTech/a-web-application-journey-index-b212e60d2440
  • Zeit Next: https://zeit.co/blog/next2
  • Universal redux - https://github.com/bdefore/universal-redux
  • react starter kit - https://github.com/kriasoft/react-starter-kit