Skip to content
This repository has been archived by the owner on May 4, 2019. It is now read-only.

react static #12

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ If you're completely new to Jekyll, I recommend checking out the documentation a

### Installing Jekyll

If you don't have Jekyll already installed, you will need to go ahead and do that.
If you don't have Jekyll already installed, you will need to go ahead and do that. I've had issues with 2.5.6 and the major ^3.0.1 upgrades.

```
$ gem install jekyll
$ gem install jekyll -v 2.4.0
```

#### Verify your Jekyll version
Expand All @@ -42,6 +42,22 @@ NOTE: passing the --drafts flag will also load all posts inside of the _drafts f
useful when you are working on a post but are not ready to publish it yet.


## Writing a Post

Make sure to have all proper markup filled out at the top of your post to get that SEO boost.

Here's a good example:
```
---
layout: post
title: Rabbits, Bunnies and Threads
author: Sai Wong
summary: When writing Ruby, we sometimes take advantage of the single threaded nature of the environment and forget some of the pitfalls of being thread safe. When using servers such as Puma that allow us to take advantage of thread to maximize on performance, we found an issue with our Bunny implementation. The issue was identified as a documented inability for Bunny channels to be shared across threads and we developed a solution to address the issue.
image: http://res.cloudinary.com/wework/image/upload/s--GnhXQxhq--/c_scale,q_jpegmini:1,w_1000/v1445269362/engineering/shutterstock_262325693.jpg
categories: ruby rails bunny rabbitmq threads concurrency puma errors
---
```

### Need a Cool Photo?

To keep some visual consistency on our blog, it is recommended to use a photo by this illustrator.
Expand Down
7 changes: 7 additions & 0 deletions _data/authors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ ramin_bozorgzadeh:
github: https://github.com/i8ramin
twitter: https://twitter.com/i8ramin
summary: Engineering Director at WeWork and loving all things performance, front-end and design related.

matt_star:
name: Matt Star
gravatar_email: [email protected]
github: https://github.com/mattjstar
twitter: https://twitter.com/mattjstar
summary: Software Engineer at WeWork. React. Redux. Front End.
223 changes: 223 additions & 0 deletions _drafts/react-static.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
layout: post
title: Building Static Sites with React, Redux, Webpack, and Roots
author: matt_star
summary: Our main marketing site (wework.com) used to be a slow monolithic Rails app. This is how we converted it to use Roots, React, and Webpack and decreased our page load speed by over 50%.
image: http://res.cloudinary.com/wework/image/upload/s--xpIlilub--/c_scale,q_jpegmini:1,w_1000/v1443207604/engineering/shutterstock_294201896.jpg
categories: engineering
---

If you read our [last post](http://engineering.wework.com/engineering/2015/12/08/why-wework-com-uses-a-static-generator-and-why-you-should-too/) you know all about why we decided to use a static site generator for the new wework.com. If you're also familiar with our [reflux to redux tutorial](http://engineering.wework.com/process/2015/10/01/react-reflux-to-redux/), you'll see that we use React and Redux to power the wework.com [Locations Flow](https://www.wework.com/locations/new-york-city/).

## Server Side Rendering

Why is server side rendering so great? Here are two examples of our [New York City](https://www.wework.com/v2/locations/new-york-city/) page.

**Without server side rendering:**

![Location Flow Without Server Rendering](http://res.cloudinary.com/wework/image/upload/s--h0Dj3ybV--/c_scale,fl_progressive,q_jpegmini,w_1000/v1449600122/engineering/Screen_Shot_2015-12-08_at_1.37.19_PM.jpg)

**With server side rendering:**

![](http://res.cloudinary.com/wework/image/upload/s--AxqGjxt---/c_scale,fl_progressive,q_jpegmini,w_1000/v1449600397/engineering/Screen_Shot_2015-12-08_at_1.45.52_PM.jpg)

When we initially built this flow, all of our React logic was being initialized through [ReactDOM.render](https://facebook.github.io/react/docs/top-level-api.html#reactdom.render). The page coming back from the server would be blank (just header and footer) and then when the DOM was ready, react would kick in and load the page accordingly. There are many great [tutorials and examples](https://github.com/DavidWells/isomorphic-react-example#other-isomorphic-tutorials--resources) on how to spin up a quick express server to render your components to string and output them as html. However, once we moved to a static site generator, we no longer had a server.

## Static React Rendering

This isn't quite server side rendering, isomorphic react, universal react, or whatever you want to call it because we don't have a server. However, this still uses the same concepts as server side rendering. We use the same logic, but instead of doing it on an express server, we pass it through a Webpack static site generator plugin to compile the html and save it to our public folder. We took a lot of cues from this excellent [tutorial on creating static pages with React components](http://jxnblk.com/writing/posts/static-site-generation-with-react-and-webpack/). We're still working on a more ideal implementation, but it boils down to the following:

* [Roots](http://roots.cx/) (our static site generator) is responsible for compiling all non-react static pages
* Webpack is responsible for compiling all views that use react
* webpack-dev-server is responsible for serving all pages (both roots and react) in development

When we compile to production, it's now as easy as running `roots compile -e production && npm run build`.

In a perfect world we'd have all the roots logic run through webpack as well. Luckily the team at carrot creative is currently working on the next implementation of Roots that does just that!


## Using React, React Router, and Redux

We made 2 major choices to extend the [tutorial](http://jxnblk.com/writing/posts/static-site-generation-with-react-and-webpack/) mentioned above on building static sites with React.

[static-site-generator-webpack-plugin](https://github.com/markdalgleish/static-site-generator-webpack-plugin)
[extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin)

```js

var path = require("path");
var oui = require('@wework/oui');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var StaticGeneratorFromUrlPlugin = require('./src/static-generator-plugin.js');

// Format url object for StaticGeneratorFromUrlPlugin plugin:
var formatUrl = function(url, token) {
return {
url: {
path: url,
headers: { 'Authorization': 'Token token=' + token },
}
}
};

var markets_api_url = process.env.DUBS_API + '/api/v1/markets'
var markets_url_object = formatUrl(markets_api_url, process.env.DUBS_API_TOKEN)

var config = {
entry: {
'base': './src/base_entry.js',
'main': './src/main_entry.js',
'home': './src/home_entry.js',
'react_base': './src/react_base_entry.js',
'market_page': './src/market_page_entry.js',
},

output: {
path: path.join(__dirname, "public"),
filename: "js/[name].bundle.js",
libraryTarget: 'umd',
},

externals: {
"_": "_",
"jquery": "jQuery",
"Modernizr": "Modernizr",
},

module: {
loaders: [
{
test: /\.jade$/,
loader: 'jade-loader',
exclude: /node_modules/
},
{
test: /\.jsx?$/,
loader: 'transform?envify!babel',
include: [
path.resolve(__dirname, "node_modules/@wework/oui/src"),
path.resolve(__dirname, "assets/js"),
path.resolve(__dirname, "src"),
],
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('css-loader'),
include: [
path.resolve(__dirname, "node_modules/@wework/oui/src"),
path.resolve(__dirname, "src"),
],
},
{
test: /\.styl$/,
loader: ExtractTextPlugin.extract('css!stylus'),
include: [
path.resolve(__dirname, "node_modules/@wework/oui/src"),
path.resolve(__dirname, "src"),
path.resolve(__dirname, "assets"),
],
},
{test: /\.json$/, loader: 'json', exclude: /node_modules/},
]
},

stylus: {
'use': [oui({ implicit: false })],
'include css': true,
},

plugins: [
new StaticGeneratorFromUrlPlugin('js/market_page.bundle.js', markets_url_object),
new ExtractTextPlugin("/css/[name].styles.css"),
]
}

module.exports = config;

```

Let's take a look at our webpack entry file to see what's going on:

```js
import React from 'react';
import ReactDOM from 'react-dom/server';
import { render as renderDOM } from 'react-dom';
import { Router, Route, match, RoutingContext } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import { Provider } from 'react-redux';

import createApiClientStore from './redux/init';
import MarketPage from './containers/MarketPage/MarketPage';

// TODO: Figure out why we need no / for
// the static site version and the extra /
// for the client side render:
const routes = ([
<Route path="/v2/locations/:market" component={MarketPage} />,
<Route path="/v2/locations/:market/" component={MarketPage} />,
]);

// Client Side Render:
if (typeof document !== 'undefined') {
// Fetch initial state from Server Rendered HTML:
const initialState = JSON.parse(window.__INITIAL_STATE__.replace(/&quot;/g, '"'));
const history = createBrowserHistory();
const store = createApiClientStore(initialState);

renderDOM(
<Provider store={store}>
<Router children={routes} history={history} />
</Provider>,
document.getElementById('content')
);
}

// Use layout.jade from roots as main layout file for react pages:
const defaultLocals = require('../lib/locals.json');
const marketsByCountry = require('../lib/marketsByCountry.json');
const template = require('../views/layout.react.jade');

// Exported static site renderer:
module.exports = function render(locals, callback) {
const initialState = {
market: {
loading: false,
data: locals.data,
},
};

// React Router 1.0 server side syntax:
// https://github.com/rackt/react-router/blob/master/docs/guides/advanced/ServerRendering.md
match({ routes, location: locals.path }, (error, redirectLocation, renderProps) => {
const store = createApiClientStore(initialState);
const initialReduxState = JSON.stringify(store.getState());

const html = ReactDOM.renderToString(
<Provider store={store}>
<RoutingContext {...renderProps} />
</Provider>
);

defaultLocals._path = '';
defaultLocals.appContent = html;
defaultLocals.description = locals.data.seo_page_title;
defaultLocals.title = locals.data.seo_page_title;
defaultLocals.initialState = initialReduxState;
defaultLocals.records = { marketsByCountry: marketsByCountry };

callback(null, template(defaultLocals));
});
};

```











2 changes: 1 addition & 1 deletion _posts/2015-04-29-caching-external-apis.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: post
title: Caching External APIs in Rails for a Ginormous Speed Boost
author: Matt Star
summary:
summary: How to use Rails Fragment Caching to cache external APIs.
image: http://res.cloudinary.com/wework/image/upload/s--unWFH26o--/c_fill,fl_progressive,g_north,h_1000,q_jpegmini,w_1600/v1430251626/engineering/caching-external-apis.jpg
categories: engineering
---
Expand Down
2 changes: 1 addition & 1 deletion _posts/2015-06-15-inside-weworks-tech-stack.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: post
title: Inside WeWork's Tech Stack
author: Matt Star
summary:
summary: Take a tour through WeWork's tech stack from one of our Lead Software Engineers.
image: http://res.cloudinary.com/wework/image/upload/s--Y5EaKyFC--/c_scale,fl_progressive,q_jpegmini:2,w_1072/v1434404739/engineering/inside-weworks-tech-stack.jpg
categories: engineering
---
Expand Down
2 changes: 1 addition & 1 deletion _posts/2015-10-01-react-reflux-to-redux.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: post
title: React Tutorial - Converting Reflux to Redux
author: Matt Star
summary:
summary: We converted our React Reflux code to React Redux, and switched over to using ES6 in the process. We'll go over how to save the state of the ui in a store, fetch data from an external API to hydrate our store, and filter data that is already in our store.
image: http://res.cloudinary.com/wework/image/upload/s--xpIlilub--/c_scale,q_jpegmini:1,w_1000/v1443207604/engineering/shutterstock_294201896.jpg
categories: process
---
Expand Down