Skip to content

Commit

Permalink
Docs: SSR README
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Apr 5, 2020
1 parent 04c33ed commit 8bfae53
Showing 1 changed file with 127 additions and 0 deletions.
127 changes: 127 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

React does not officially support using Suspense for data fetching. This package makes that possible.

It also supports [server-side rendering](#server-side-rendering) using [react-async-ssr](https://www.npmjs.com/package/react-async-ssr).

NB Does **not** require experimental builds of React, or "concurrent mode". Tested and working on all versions of React 16.8.0+.

## Usage
Expand Down Expand Up @@ -372,6 +374,131 @@ isResource( resource ) // => `true`
isResource( { foo: 'bar' } ) // => `false`
```
### Server-side rendering
Server-side rendering is supported by this package when using [react-async-ssr](https://www.npmjs.com/package/react-async-ssr) rather than React's native rendering methods on server.
There are 3 elements to making server-side rendering work:
1. Give each Resource Factory a unique name
2. Capture data loaded on server and transmit it to browser along with HTML
3. Load the data cache in browser with server-loaded data before hydration
This package provides methods to do all 3 of these things.
NB Server side rendering automatically enables [caching](#caching).
#### 1. Naming Resource Factories
All resource factories must be given a name, using the `name` option. Every call to `createResourceFactory()` must have a name which **must** be unique within the whole application.
```js
import { createResourceFactory } from 'react-lazy-data';

// Create Resource Factory
const PokemonResource = createResourceFactory(
id => fetch(`https://pokeapi.co/api/v2/pokemon/${id}`).then(res => res.json()),
{ name: 'pokemon' }
);
```
To make this easier, you can use a Babel plugin provided by this package.
```js
// babel.config.js
{
"plugins": [
"react-lazy-data/babel"
]
}
```
This will add a unique name to every call to `createResourceFactory()`. The names are a hash of the file's path relative to the application's root and a counter. They are deterministic across builds and machines.
#### 2. Capture data loaded on server side
The data which is loaded on the server needs to be sent to the browser so it can be used again when hydrating the app.
The first step is to capture this data on the server.
Create a `DataExtractor` and then wrap the app with its `.collectData()` method. Then call the extractor's `.getScript()` method to get HTML to add to the server response.
```js
// Server-side code
import { DataExtractor } from 'react-async-ssr/server';
import { renderToStringAsync } from 'react-lazy-data/server';
import App from './App.js';

async function render() {
const extractor = new DataExtractor();
const html = await renderToStringAsync(
extractor.collectData(
<App />
)
);

return `
<html>
<body>
<div id="root">${html}</div>
${extractor.getScript()}
</body>
</html>
`;
}
```
The output of `render()` will be something like:
```html
<html>
<body>
<div id="root">
<div>My name is bulbasaur</div>
<!-- ^^^ Rendered HTML ^^^ -->
</div>
<script>
(window["__react-lazy-data.DATA_CACHE"] = window["__react-lazy-data.DATA_CACHE"] || []).push(
{
"pokemon": { // <-- Name of resource factory
"1": { // <-- Serialized request
"name":"bulbasaur" // <-- Data for this request
}
}
}
);
</script>
</body>
</html>
```
#### 3. Load data into cache on client side
The data sent from the server in the script must be injected into the app before it is hydrated on client side.
Run `preloadData()` and await its promise before `ReactDOM.hydrate()`.
```js
// Client-side code
import React from 'react';
import ReactDOM from 'react-dom';
import { preloadData } from 'react-lazy-data';
import App from './App.js';

preloadData().then(() => {
ReactDOM.hydrate(
<App />,
document.getElementById('root')
);
});
```
This last step is not required if the script tag created by `extractor.getScript()` is inserted into the HTML *before* Javascript script tags.
However, performance is usually better if you put Javascript bundle scripts at the top of the document with `<script async>` or `<script defer>`. Then the Javascript may run before the data script executes, which would mean the application would attempt to hydrate without data, causing hydration errors.
In this case, `preloadData()` is required to make it wait for the data before hydrating.
## Versioning
This module follows [semver](https://semver.org/). Breaking changes will only be made in major version updates.
Expand Down

0 comments on commit 8bfae53

Please sign in to comment.