You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/recipes/ServerRendering.md
+21-57Lines changed: 21 additions & 57 deletions
Original file line number
Diff line number
Diff line change
@@ -1,22 +1,25 @@
1
1
# Server Rendering
2
2
3
-
## Redux and Server Side Rendering
4
-
Redux is well-suited for server side rendering. This is because Redux does not rely on _singletons_.
3
+
The most common use case for server-side rendering is to handle the _initial render_ when a user (or search engine crawler) first requests our app. When the server receives the request, it renders the required component(s) into an HTML string, and then sends it as a response to the client. From that point on, the client takes over rendering duties.
5
4
6
-
Redux's job on the server side is to provide the initial state for the initial render of our app. To get this, we need to create a new instance of a Redux store for every new request.
5
+
### Redux on the server
6
+
When using a store like Redux, we must also send the initial state of our app along in our response. To do this, we need to: create a fresh, new Redux store instance on every request, optionally dispatch some actions, pull the state out of store, and then pass the state along to the client. On the client side, a new Redux store will be created and initialized with the state provided from the server.
7
7
8
-
In the following recipe, we are going to look at how to set up server side rendering. We are going to use the code from our Todo List app that we built in Basics as a guide, but shouldn't need to touch any of the actions, reducers, or components from that tutorial.
8
+
Redux's **_only_** job on the server side is to provide the **initial state** of our app.
9
+
10
+
-----
11
+
12
+
In the following recipe, we are going to look at how to set up server-side rendering, using the [Todo List app](../basics/ExampleTodoList.html) that we built in [Basics](../basics/) as a guide.
9
13
10
14
## Setting Up
11
15
12
16
### File Structure
13
-
We are going to move our actions, reducers, routes, and components from our Todo List app into a `shared/` folder, since they will be used by both the client and server. Note that we have moved our client-side entrypoint into the `client/` folder.
17
+
We are going to put the actions, reducers, and components from the Todo List app into a `shared/` folder, since they will be used by both the client and server. Note that we have moved our client-side entrypoint into the `client/` folder.
14
18
15
19
server.jsx
16
20
client/index.jsx
17
21
shared/actions.js
18
22
shared/reducers.js
19
-
shared/routes.js
20
23
shared/containers/App.js
21
24
shared/components/AddTodo.js
22
25
shared/components/Footer.js
@@ -25,45 +28,26 @@ We are going to move our actions, reducers, routes, and components from our Todo
25
28
26
29
27
30
### Install Packages
28
-
For this example, we will be using [Express](http://expressjs.com/) as a simple web server.
29
-
30
-
We are going to be using [React Router v. 1.0.0-beta3](https://rackt.github.io/react-router/) to handle both our back-end and front-end routes.
31
+
For this example, we'll be using [Express](http://expressjs.com/) as a simple web server.
31
32
32
33
We also need to install the React bindings for Redux, since they are not included in Redux by default.
The following is the outline for what our server side is going to look like. We are going to set up an [Express middleware](http://expressjs.com/guide/using-middleware.html) using [app.use](http://expressjs.com/api.html#app.use) to handle all requests that come in to our server. If you're unfamiliar with Express or middleware, just know that our handleRender function will be called every time the server receives a request.
58
44
```js
59
45
importExpressfrom'express';
60
46
importReactfrom'react';
61
-
import { Router } from'react-router';
62
-
importLocationfrom'react-router/lib/Location';
63
47
import { createStore } from'redux';
64
48
import { Provider } from'react-redux';
65
-
importroutesfrom'./shared/routes';
66
49
importtodoAppfrom'./shared/reducers';
50
+
importAppfrom'./shared/containers/App';
67
51
68
52
var app =Express();
69
53
@@ -72,46 +56,30 @@ app.use(handleRender);
72
56
73
57
// We are going to fill these out in the sections to follow
Every time a request is received, the first thing we need to do is create a new Redux store instance. The only purpose of this store instance is to provide the initial state of our application. Next, we pass our routes and the requested location to React Router. React Router is going to look at our **routes.js** and fetch the component that should handle the requested route.
84
-
```js
85
-
functionhandleRender(req, res) {
86
-
// Create a new Redux store instance
87
-
var store =createStore(todoApp);
88
-
89
-
// Create a new Location based on the requested path
90
-
var location =newLocation(req.path, req.query);
66
+
The first thing that we need to do on every request is create a new Redux store instance. The only purpose of this store instance is to provide the initial state of our application.
91
67
92
-
// Run React Router with the given location and our routes
93
-
// renderRoute is the callback that will be fired
94
-
Router.run(routes, location, renderRoute);
95
-
};
96
-
```
97
-
98
-
**Rendering the Component**
68
+
When rendering, we will wrap `<App/>`, our root component, inside a `<Provider>` to make the store available to all components in the component tree.
99
69
100
70
The key step in server side rendering is to render the initial HTML of our component _**before**_ we send it to the client side. To do this, we use [React.renderToString](https://facebook.github.io/react/docs/top-level-api.html#react.rendertostring).
101
71
102
-
We wrap our root component in Provider to make the store available to all components in the component tree.
72
+
We then get the initial state from our Redux store using **store.getState()**. We will see how this is passed along in our `renderFullPage` function.
103
73
104
-
We also need to get the initial state from our Redux store, so that we can pass it along to the client as well. We get the initial state using **store.getState()**. We will see how this is passed along in our _renderFullPage_ function.
105
74
```js
106
-
functionrenderRoute(err, routeState) {
107
-
if(err) returnconsole.error(err);
75
+
functionhandleRender(req, res) {
76
+
// Create a new Redux store instance
77
+
var store =createStore(todoApp);
108
78
109
79
// Render the component to a string
110
80
var html =React.renderToString(
111
81
<Provider store={store}>
112
-
{() =>
113
-
<Router {...routeState} />
114
-
}
82
+
{ () =><App/> }
115
83
</Provider>);
116
84
117
85
// Grab the initial state from our Redux store
@@ -148,21 +116,17 @@ function renderFullPage(html, initialState) {
148
116
149
117
## The Client Side
150
118
151
-
The client side is very straightforward. All we need to do is _hydrate_ our store by grabbing `window.__INITIAL_STATE__` and passing it to our createStore function as the initial state.
152
-
153
-
We also need to pass the Router in as our child to Provider, rather than our App component.
119
+
The client side is very straightforward. All we need to do is grab the initial state from `window.__INITIAL_STATE__`, and pass it to our createStore function as the initial state.
0 commit comments