TomokiMiyauci / react-patial-hydration

React component for partial hydration

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

react-partial-hydration

React component to delay or skip hydration.

It can be combined with lazy component to achieve the best performance. See React.lazy for details.

Quick view

Install

yarn add react-partial-hydration

or

npm i react-partial-hydration

Static

The children of the Static component will skip hydration.

import { Static, Intersection } from 'react-partial-hydration'
;<Static>{children}</Static>

Intersection

The children of the Intersection component will delay hydration until it intersects the viewport.

import { Intersection } from 'react-partial-hydration'
;<Intersection>{children}</Intersection>

API

This provide components as below:

  • Static
  • Intersection

Static

No(skip) hydration component

If the component does not need to be reactive, it can be optimized for performance.

import { Static } from 'react-partial-hydration'
;<Static>{children}</Static>

props

Prop Default Description
children - JSX.Element
The children component
as div keyof ReactHTML ?
Static component should render as.
fallback children false | JSX.Element ?
When The DOM is not exists, fallback to children or passed component or not.
onFallback - () => void ?
On fallback component is rendered, then fire
...props - DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> ?
All attributes to render Static component.

SSR render

By default, on the server side, it will be rendered as follows:

<div style="display:contents">{children}</div>

The only drawback now is that it requires a wrapper HTML tag. Fortunately, display:contents does not break the style of children. Also, there will probably be no impact on SEO.

CSR render

On the client side, skip hydration and keep the DOM tree.

<div style="display:contents">{children}</div>

Intersection

The component to delay hydration until it intersects the viewport.

import { Intersection } from 'react-partial-hydration'
;<Intersection>{children}</Intersection>

props

Prop Default Description
children - JSX.Element
The children component.
as div keyof ReactHTML ?
Intersection component should render as.
fallback children false | JSX.Element ?
When The DOM is not exists, fallback to children or passed component or not.
onFallback - () => void ?
On fallback component is rendered, then fire.
target <div /> JSX.Element ?
Target used for intersection.
root - Element | Document | null ?
The element that is used as the viewport for checking visibility of the target.
rootMargin - string ?
Margin around the root.
threshold - number | number[] ?
Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed.
...props - DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> ?
All attributes to render Intersection component.

SSR render

By default, on the server side, it will be rendered as follows:

<div style="display:contents">
  <div />
  {children}
</div>

The only drawback now is that it requires a wrapper HTML tag. Fortunately, display:contents does not break the style of children. Also, there will probably be no impact on SEO.

CSR render

On the client side, hydration is delayed until Intersect.

<div style="display:contents">
  <div />
  {children}
</div>

Architecture

Normally, if a react component does not return any DOM Elements, it will be removed from the DOM or React will report an error.

For example, the following component will cause a flush and be removed from the DOM tree in browser.

import type { FC } from 'react'
const FlashThenRemove: FC = () => {
  if (isServer) {
    return <div>This will delete from DOM</div>
  }
  return <></>
  // or return void
}

SSR:

<div>This will delete from DOM</div>

CSR:

// Flash and disappear

In order to maintain the DOM tree without using hydrate or render, we can use dangerouslySetInnerHTML.

const NoHydrate: FC = () => {
  if (isServer) {
    return <div>This will not delete from DOM</div>
  }
  return (
    <div
      dangerouslySetInnerHTML={{
        __html: ''
      }}
    />
  )
}

It relies on the original html. On the server side and the client side, the parent component must be the same HTML tag.

For example, in the above example, if you change the div tag in the server-side generated html to a span tag, nothing will be rendered on the client side.

The fallback system

From the above example, we can see that hydration skipping depends on the original html. If the html is not what you expect, it will disappear from the DOM tree. This can be a problem.

The best example of where this situation occurs is when you are using an App Shell.

App Shell uses a Service worker to get assets from cache storage so that the app can work offline.

In this case, the original html DOM tree is empty unless you make your own changes, because App Shell is based on CSR.

To work correctly in this situation, all components have a fallback system implemented.

This means that if the correct DOM is not rendered, hydration will occur and the correct DOM will be corrected.

The fallback props allows you to specify which components to fallback. The default is children. You can disable fallback by setting it to false.

Lazy component

By using these components, hydration will be delayed or not occur. This will improve performance.

However, the fetch of the component will still take place.

Let's look at the following example.

import type { FC } from 'react'
type Props = {
  title: string
  description: string
}
const Headline: FC<Props> = ({ title, description }) => {
  return (
    <article>
      <h2>{title}</h2>
      <p>{description}</p>
    </article>
  )
}
export default Headline
import Headline from 'path/to/headline'
import { Static } from 'react-partial-hydration'

<Static>
  <Headline title="Hello" description="World">
</Static>

The Headline component is not hydrated by the Static component. This is certainly a good thing for performance. However, since the Headline component is not chunked, it will be included in the main bundle.

Even though it is not used for rendering, it increases the main bundle and the user is fetching unnecessary scripts.

To remedy this, we will use the lazy component.

React.lazy

The React.lazy function can create lazy components.

The lazy component is characterized by the fact that it will be fetched only when it is needed.

For example, let's look at the following example:

import { lazy, useState } from 'react'

const Headline = lazy(() => import('path/to/headline'))

const Main: FC = () => {
  const [isShow, changeShow] = useState(false)

  return isShow && <Headline title="Hello" description="World">
}

For the Headline component, the component fetch itself will be delayed until the isShow is true.

Unfortunately, React.lazy doesn't support SSR yet.

Third-party lazy components

Loadable Components is the officially recommended Code Splitting library that supports SSR. By using it, you can render lazy components on the server side.

There are no dependencies in this project. You can also use solutions other than Loadable Components.

The rendering of lazy components on the server side depends on the bundler.

You can see how to use Loadable Components on the server side in the following example.

  • Gatsby [WIP]
  • Next.js [WIP]

Once your application is ready, you can use it as follows:

import loadable from '@loadable/component'
import { Static } from 'react-partial-hydration'
const Headline = loadable(() => import('path/to/headline'))

<Static>
  <Headline title="Hello" description="World">
</Static>

This will cause the markup to be rendered on the server side. And on the client side, the fetch and hydration of the component will be skipped.

Similarly for the Intersection component, no fetch or hydration will be done until it is intersected.

Contributing

Contributions, issues and feature requests are welcome!
Feel free to check issues.

Show your support

Give a ⭐️ if this project helped you!

License

Copyright © 2021-present TomokiMiyauci.

Released under the MIT license

About

React component for partial hydration

License:MIT License


Languages

Language:TypeScript 100.0%