Skip to content

Instantly share code, notes, and snippets.

@OliverJAsh
Created October 5, 2023 08:32
Show Gist options
  • Save OliverJAsh/58af4f7ab5ac6cc7584ef78f16462ba9 to your computer and use it in GitHub Desktop.
Save OliverJAsh/58af4f7ab5ac6cc7584ef78f16462ba9 to your computer and use it in GitHub Desktop.

Revisions

  1. OliverJAsh created this gist Oct 5, 2023.
    48 changes: 48 additions & 0 deletions SkipRenderOnClient.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    import * as React from 'react';

    const useIsFirstRender = (): boolean => {
    const isFirst = React.useRef(true);

    if (isFirst.current) {
    isFirst.current = false;

    return true;
    } else {
    return false;
    }
    };

    /**
    * Use case:
    * - A component must be rendered on the server.
    * - On the client-side, rendering is conditional e.g. based on window width.
    * - However, in order for hydration to succeed, the first client render must
    * match the DOM generated from the HTML returned by the server. This means
    * the component must be rendered again during the first client render and
    * removed afterwards.
    * - Hydration is expensive, so we really don't want to pay that penalty only
    * for the element to be removed immediately afterwards.
    *
    * This component conditionally skips hydrating children by removing them from
    * the DOM _before the first client render_. Removing them before ensures
    * hydration is successful.
    */
    export const SkipRenderOnClient: React.FC<{
    children: React.ReactNode;
    shouldRenderOnClient: IO<boolean>;
    }> = ({ children, shouldRenderOnClient }) => {
    const id = React.useId();
    const isBrowser = typeof window !== 'undefined';
    const isFirstRender = useIsFirstRender();

    if (isBrowser && isFirstRender && shouldRenderOnClient() === false) {
    const el = document.getElementById(id);
    if (el !== null) {
    el.innerHTML = '';
    }
    }

    const shouldRender = isBrowser ? shouldRenderOnClient() : true;

    return <div id={id}>{shouldRender && children}</div>;
    };