Skip to content

Commit

Permalink
Improve async component section (stereobooster#377)
Browse files Browse the repository at this point in the history
  • Loading branch information
stereobooster authored May 20, 2019
1 parent 6642abf commit 377015c
Showing 1 changed file with 48 additions and 37 deletions.
85 changes: 48 additions & 37 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,55 @@ It is not a problem to render async components with `react-snap`, the tricky par
0% -------------/
```

Usually _code splitting_ library provides an API to handle it during SSR, but as long as "real" SSR is not used in react-snap - the issue surfaces, and there is no simple way to fix it.
Usually a _code splitting_ library provides an API to handle it during SSR, but as long as "real" SSR is not used in react-snap - the issue surfaces, and there is no simple way to fix it.

1. use `loadable-components` 2.2.3 (current is >5)
The old version of `loadable-components` can solve this issue for a "snapshot" setup:
1. Use [react-prerendered-component](https://github.com/theKashey/react-prerendered-component). This library holds onto the prerendered HTML until the dynamically imported code is ready.

```js
import loadable from "@loadable/component";
import { PrerenderedComponent } from "react-prerendered-component";

const prerenderedLoadable = dynamicImport => {
const LoadableComponent = loadable(dynamicImport);
// you can use the `.preload()` method from react-loadable or react-imported-component`
const live = () => LoadableComponent.load();
return React.memo(props => (
<PrerenderedComponent live={live}>
<LoadableComponent {...props} />
</PrerenderedComponent>
));
};

const MyComponent = prerenderedLoadable(() => import("./MyComponent"));
```

`MyComponent` will use prerendered HTML to prevent the page content from flashing (it will find the required piece of HTML using an `id` attribute generated by `PrerenderedComponent` and inject it using `dangerouslySetInnerHTML`).

2. The same approach will work with `React.lazy`, but `React.lazy` doesn't provide a prefetch method (`load` or `preload`), so you need to implement it yourself (this can be a fragile solution).

```js
const prefetchMap = new WeakMap();
const prefetchLazy = LazyComponent => {
if (!prefetchMap.has(LazyComponent)) {
prefetchMap.set(LazyComponent, LazyComponent._ctor());
}
return prefetchMap.get(LazyComponent);
);

const prerenderedLazy = dynamicImport => {
const LazyComponent = React.lazy(dynamicImport);
const live = () => prefetchLazy(AsyncLoadedComponent)
return React.memo(props => (
<PrerenderedComponent live={live}>
<LazyComponent {...props} />
</PrerenderedComponent>
));
};

const MyComponent = prerenderedLazy(() => import("./MyComponent"));
```
3. use `loadable-components` 2.2.3 (current is >5). The old version of `loadable-components` can solve this issue for a "snapshot" setup:
```js
import { loadComponents, getState } from "loadable-components";
Expand All @@ -187,40 +232,6 @@ const NotFoundPage = loadable(() => import("src/pages/NotFoundPage"), {
> `loadable-components` were deprecated in favour of `@loadable/component`, but `@loadable/component` dropped `getState`. So if you want to use `loadable-components` you can use old version (`2.2.3` latest version at the moment of writing) or you can wait until `React` will implement proper handling of this case with asynchronous rendering and `React.lazy`.
2. Use [react-prerendered-component](https://github.com/theKashey/react-prerendered-component)
This library could _keep_ the pre-rendered HTML until codesplitted part is not loaded and ready.
```js
import {PrerenderedComponent} from 'react-prerendered-component';

const AsyncLoadedComponent = someLibrary(() => import('./deferredComponent'));

<PrerenderedComponent
live={AsyncLoadedComponent.preload()} // for react-loadable or react-imported-component
live={AsyncLoadedComponent.load()} // for @loadable/components (not official way)
>
<AsyncLoadedComponent />
</PrerenderedComponent>
```
Until `import promise` would not resolved - it would display content, react-snap have pre-rendered for you.

The same apporoach would work with `React.lazy`, but it does not support `prefetch`, so you will have to do it yourself (fragile!).
```js
const PrefetchLazy = lazy => {
let value = PrefetchMap.get(lazy) || lazy._ctor();
PrefetchMap.set(lazy, value);
return value;
);

// using React.lazy
const AsyncLoadedComponent = React.lazy(() => import('./deferredComponent'));

<PrerenderedComponent
live={PrefetchLazy(AsyncLoadedComponent)}
>
<AsyncLoadedComponent />
</PrerenderedComponent>
```
### Redux
See: [Redux Server Rendering Section](https://redux.js.org/docs/recipes/ServerRendering.html#the-client-side)
Expand Down

0 comments on commit 377015c

Please sign in to comment.