Socket
Socket
Sign inDemoInstall

react-async-ssr

Package Overview
Dependencies
15
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.7.0 to 0.7.1

6

changelog.md
# Changelog
## 0.7.1
Docs:
* Update docs
## 0.7.0

@@ -4,0 +10,0 @@

2

package.json
{
"name": "react-async-ssr",
"version": "0.7.0",
"version": "0.7.1",
"description": "Render React Suspense on server",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -31,6 +31,6 @@ [![NPM version](https://img.shields.io/npm/v/react-async-ssr.svg)](https://www.npmjs.com/package/react-async-ssr)

```jsx
const ReactDOMServer = require('react-dom/server');
const { renderToString } = require('react-dom/server');
function render() {
const html = ReactDOMServer.renderToString( <App /> );
const html = renderToString( <App /> );
return html;

@@ -43,6 +43,6 @@ }

```jsx
const ReactDOMServer = require('react-async-ssr');
const { renderToStringAsync } = require('react-async-ssr');
async function render() {
const html = await ReactDOMServer.renderToStringAsync( <App /> );
const html = await renderToStringAsync( <App /> );
return html;

@@ -78,87 +78,12 @@ }

[@loadable/component](https://www.smooth-code.com/open-source/loadable-components/docs/api-loadable-component/#lazy) provides a `.lazy()` method which is equivalent to `React.lazy()` but suitable for server-side rendering. [This guide](https://www.smooth-code.com/open-source/loadable-components/docs/server-side-rendering/) explains how to use it for server-side rendering.
[react-lazy-ssr](https://www.npmjs.com/package/react-lazy-ssr) is a drop-in replacement for `React.lazy()`, designed to work with `react-async-ssr`.
### Lazy data
`.renderToStringAsync()` supports any component which fits within React's convention for suspendable components.
[react-lazy-data](https://www.npmjs.com/package/react-lazy-data) is a ready-to-go simple solution for async data loading which is designed to work with `react-async-ssr`.
In its `render()` method, the component should throw a Promise which will resolve when the data is loaded. When the promise resolves, the renderer will re-render the component and add it into the markup.
If you're brave, you could also [roll your own](#rolling-your-own-lazy-data-solution) or adapt another existing data-loading package to work with `react-async-ssr`.
#### Basic example
If you have success, please [let me know](https://github.com/overlookmotel/react-async-ssr/issues/66).
```jsx
let data = null, promise;
function LazyData() {
if (data) return <div>{ data.foo }</div>;
if (!promise) {
promise = new Promise( resolve => {
setTimeout( () => {
data = {foo: 'bar'};
resolve();
}, 1000 );
} );
}
throw promise;
}
function App() {
return (
<div>
<Suspense fallback={ <div>Loading...</div> }>
<LazyData />
</Suspense>
</div>
);
}
const html = await ReactDOMServer.renderToStringAsync( <App /> );
// html === '<div>bar</div>'
```
#### react-cache example
An example using the experimental package [react-cache](https://www.npmjs.com/package/react-cache):
```jsx
const { createResource } = require('react-cache');
const PokemonResource = createResource(
id =>
fetch(`https://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
);
function Pokemon(props) {
const data = PokemonResource.read(props.id);
return <div>My name is {data.name}.</div>;
}
function App() {
return (
<div>
<Suspense fallback={ <div>Loading...</div> }>
<Pokemon id={1} />
<Pokemon id={2} />
<Pokemon id={3} />
</Suspense>
</div>
);
}
const html = await ReactDOMServer.renderToStringAsync( <App /> );
// html === `
// <div>
// <div>My name is bulbasaur.</div>
// <div>My name is ivysaur.</div>
// <div>My name is venusaur.</div>
// </div>
// `
```
The above example makes 3 async fetch requests, which are made in parallel. They are awaited, and the HTML markup rendered only once all the data is ready.
### Hydrating the render on client side

@@ -186,7 +111,7 @@

In the example above, the `<Pokemon>` component is completely independent. You can drop it in to any app, anywhere in the component tree, and it'll be able to load the data it needs, without any complex "wiring up".
You can create components which load their own data and are completely independent. You can drop these components in to any `react-async-ssr`-enabled app, anywhere in the component tree, and they'll be able to load the data they needs, without any complex "wiring up".
However, some mechanism is required to gather the data loaded on the server in order to send it to the client for hydration.
There are many solutions, for example using a [Redux](https://redux.js.org/) store, or a [Context](https://reactjs.org/docs/context.html) Provider at the root of the app. This package does not make any assumptions about how the user wants to handle this, and no doubt solutions will emerge from the community. All that this package requires is that components follow React's convention that components wishing to do async loading throw promises.
[react-lazy-ssr](https://www.npmjs.com/package/react-lazy-ssr) and [react-lazy-data](https://www.npmjs.com/package/react-lazy-data) both provide mechanisms for this. But you could use other methods. This package does not make any assumptions about how it's done - all it requires is that components follow React's convention that components wishing to do async loading throw promises, and provides some [hooks](#tracking-components-being-used) to enable tracking what's loaded on the server.

@@ -212,3 +137,3 @@ ### Complicated cases

`[ON_MOUNT]()` is called with `true` if the element will be rendered on client, or `false` if it will not. `false` happens if the promise was thrown by a component which ends up being inside a Suspense boundary whose fallback is triggered, so the component is not rendered.
`[ON_MOUNT]()` is called with `true` if the element is needed for initial render on the client, or `false` if it will not. `false` happens if the promise was thrown by a component which ends up being inside a Suspense boundary whose fallback is triggered, so the component is not rendered.

@@ -235,3 +160,3 @@ Only components whose promise's `[ON_MOUNT]()` method has been called with `true` should have their imported file/data provided to client so they can be rehydrated synchronously. Those called with `false` should be allowed to load file/data asynchronously.

function LazyNoSSR() {
const promise = new Promise(() => {});
const promise = new Promise( () => {} );
promise[NO_SSR] = true;

@@ -250,3 +175,3 @@ throw promise;

When rendered on server, this will output `<div>Loading...</div>`. The content can then be loaded client side after hydration.
When rendered on server, this will output `<div>Loading...</div>`. The content can then be loaded on client side after hydration.

@@ -269,3 +194,3 @@ On client side, to ensure hydration completes correctly, the component must throw a promise which then resolves to the required component/data, and not render the content synchronously.

As an optimization, you can cause the render to bail out of rendering all further content within the Suspense as soon as the fallback is triggered, by providing a `fallbackFast` option to `.renderToStringAsync()`.
As an optimization, you can cause the render to bail out of rendering all further content within the Suspense boundary as soon as the fallback is triggered, by providing a `fallbackFast` option to `.renderToStringAsync()`.

@@ -282,5 +207,5 @@ ```jsx

const html = await ReactDOMServer.renderToStringAsync(
const html = await renderToStringAsync(
<App />,
{fallbackFast: true}
{ fallbackFast: true }
);

@@ -323,6 +248,71 @@ ```

In the first render pass, all the promises for async-loaded data are collected. Once all the promises resolve, a 2nd render pass produces the actual HTML markup which is sent to the client. Obviously, it's resource-intensive to render twice. And if async components themselves make further async requests, 3rd or 4th or more render passes can be required.
In the first render pass, all the promises for async-loaded data are collected. Once all the promises resolve, a 2nd render pass produces the actual HTML markup which is sent to the client. Obviously, it's resource-intensive to render twice. And if async components themselves make further async requests, 3rd or 4th render passes can be required.
The `.renderToStringAsync()` method provided by this package renders in a single pass. The render is interrupted when awaiting an async resource and resumed once it has loaded.
### Rolling your own lazy data solution
`.renderToStringAsync()` supports any component which fits within React's convention for suspendable components.
In its `render()` method, the component should throw a Promise which will resolve when the data is loaded. When the promise resolves, the renderer will re-render the component and add it into the markup.
Here's a basic example:
```jsx
import React, { createContext } from 'react';
import { renderToStringAsync } from 'react-async-ssr';
const PokemonDataContext = createContext();
function Pokemon( { id } ) {
const store = useContext( PokemonDataContext );
let entry = store[id];
if ( !entry ) entry = store[id] = {};
const { data } = entry;
if ( data ) return <div>My name is: { data.name }</div>;
let { promise } = entry;
if ( !promise ) {
promise = fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
.then( res => res.json() )
.then( data => {
entry.data = data;
entry.promise = undefined;
} );
entry.promise = promise;
}
throw promise;
}
function App() {
return (
<div>
<PokemonDataContext.Provider value={{}}>
<Suspense fallback={ <div>Loading...</div> }>
<Pokemon id={1} />
<Pokemon id={2} />
<Pokemon id={3} />
</Suspense>
</PokemonDataContext.Provider>
</div>
);
}
const html = await renderToStringAsync( <App /> );
// html === `
// <div>
// <div>My name is bulbasaur.</div>
// <div>My name is ivysaur.</div>
// <div>My name is venusaur.</div>
// </div>
// `
```
The above example makes 3 async fetch requests, which are made in parallel. They are awaited, and the HTML markup rendered only once all the data is ready.
A full solution is more complex than this, but the above shows the gist.
## Versioning

@@ -329,0 +319,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc