Peter Chang

@peterchang_82818

Break Down Isomorphic and Universal Boilerplate: React-Redux server rendering

March 28th 2017
React Redux server rendering structure
Looking for a React-Redux Boilerplate? If you are, you probably had no problem finding one. In fact, you probably found TONS of them (From Andrew).

You also like

Since React and Redux was born, I was hooked on the paradise of 1)React Isomorphic in which we get speed and SEO benefit of rendering on the server and you can still render the shared components after the page loads on the client and 2) Ideas of Flux architecture, which complements React’s composable view components by utilizing a unidirectional data flow.

After I pulled up thousands of isomorphic and flux boilerplate, I made a cup of tea to comfort myself, because non of them is short, the learning curve are distressingly steep, it means that there is not a shortcut and I need to start it from the ground up. This article is a note recorded the process how the boilerplate is broken down to understandable pieces, little by little. At the end, I concluded 5 big issue(functionalities or tags), they are the reason why creating so many isomorphic boilerplates:

  1. Dev-Server &Build Bundle:
    Setup development and production environment, mainly to solve the problem of hot-reload, auto-watch, bundle build etc. Popular tools: webpack, gulp, nodenpm, browserify and grunt.
  2. *React-Redux Components and Data flow
  3. Routing
    To decide what routing hierarchy is used, we should ask ourself few questions: Is it a single page website? Dose the API server support cross-origin? Is wrapping inside express server better than client side?
  4. Share Reducer/Action Creator
    It is related to Routing
  5. Test

Here, we are going to talk about the 2nd point, it is the only one common part amount the thousand of boilerplate, and the rest of four are changed variedly by different preference of developers. Server-side rendering, shared components and Redux module are the core of isomorphic and flux architecture, that is the reason why every boilerplate is using the modules of ‘react’, ‘redux’ and ‘react-redux’.

Clone the repository from Github, a hellowWorld example which base on React-Redux, and we are going to break it down to pieces to understand how each file works:

React Redux server rendering structure

Above is the structure of how the whole app works, the app bases on Express web framework, which serves only one route, with res.sendFile function to put index.html into the browser. Inside the scoop of the structure, what we are interested is the blue box, the interaction between react component, redux, root component, store and reducer.

React Redux Structure of data, store, props, state and component

This is an example followed by official documentation, the Facebook team proposes couple of advise and design principle to the hierarchy of components, for example: separated container and presentational components, use connect() rather than store.subscribe() etc.

index.js, as the entry file and a high level root component, which gathers all the sub-component as the subtree of the Virtual DOM, also it is the only file entangled with many independent modules. Apart from it, different file requires independent modules, which makes clean code and work independently.

Up to here, I really feel that Facebook have done a lot to us, developers.

Different file response for independent work

Below are the functions which play important roles in react-redux components:

<Provider>

It magically make the store available to all container components in the application without passing it explicitly. You only need to use it once when you render the root component:

import { Provider } from 'react-redux'
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

connect( … )

Connecting a React component to Redux allows the component to use the top-level store without having to pass the store down as a prop through its parent components (from).

  1. Inject store to Root Component

To start connecting components, we have to wrap our root component in Provider, and pass it the store variable:

import helloReducer from './reducers'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
let store = createStore(helloReducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

2. Connecting store to presentational components

The React Redux docs describe many ways to use connect. To my purpose, only mapStateToProps and mapDispatchToProps are needed to fulfill the functionality.

— mapping state to props will provide the Hello component this.props.message from the Redux store

— mapping dispatch functions for HELLO_WORLD actions to props will make this.props.onClick available as functions in component Hello.

const mapStateToProps = (state, ownProps) => {
return {
message: state.helloWorld.message
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch({ type: HELLO_WORLD })
}
}
}
const HelloWorld = connect(
mapStateToProps,
mapDispatchToProps
)(Hello)

Must know NPM Module

Below are some NPM modules which many people may not know:

react-redux

Redux&React bindings are not included in Redux by default. You need to install npm package react-redux explicitly. 
This assumes that you’re using npm package manager with a module bundler like Webpack or Browserify to consume CommonJS modules.

webpack-dev-middleware

It’s a simple wrapper middleware for webpack. It serves the files emitted from webpack over a connect server. This should be used for development only (more).
* No files are written to disk, it handle the files in memory

webpack-hot-server-middleware

It is designed to be used in conjunction with webpack-dev-middleware, to hot update Webpack bundles on the server (more).

Remark

Below are some concept and terminology:

HMR

stands for ‘Hot Module Replacement’, sometimes called ‘hot module swapping’. It’s a Webpack feature that updates your JavaScript in-place without a browser refresh (more).

combineReducers( ... )

It turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.

createStore (reducer, [preloadedState], [enhancer])

  1. To create a Redux store that holds the complete state tree of your app.
  2. the createStore(reducer, [initialState], [enhancer]) which will be passed in <Provider>:
import { createStore, combineReducers } from 'redux'
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([ action.text ])
default:
return state
}
}
function prefixTodos(state = [], action) {
switch (action.type) {
case 'PRE_ADD_TODO':
return state.concat([ 'pre_'+action.text ])
default:
return state
}
}
const mixReducers= combineReducers({todos, prefixTodos})
let store = createStore(mixReducers, [ 'Use Redux' ])

subscribe( … ) VS connect(…)

They are doing the same things in Redux, but React officially announces that NOT advise you to use store.subscribe(), for the reasons that React Redux makes many performance optimizations that are hard to do by hand.

connect() , generally we generate container components with connect() to connect them to the Redux store.

Container Components VS Presentational Components

Components could be divided into two categories, I also heard Fat and Skinny, Smart and Dumb, Stateful and Pure, Screens and Components (from):

  • Container Components: Provide the data and behavior to presentational or other container components.
  • Presentational Components: 1) Have no dependencies on the rest of the app, 2) Are concerned with how things look.
Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Peter Chang

More Related Stories