Home > Back-end >  How to lazy-load a React "widget"?
How to lazy-load a React "widget"?

Time:07-10

My terminology is probably wrong here, but I don't know what to call it other than a "widget" when you don't have a whole React app, but are attaching little React pieces to different roots on an otherwise static HTML page. But, that's what I'm doing:

const StripeForm = React.lazy(() => import('./Stripeform'));
// ...
const formPlaceholder = document.getElementById('cwr-stripe-form');
const formRoot = createRoot(formPlaceholder);
formRoot.render(
  <React.StrictMode>
    <StripeForm />
  </React.StrictMode>
);

The problem I'm trying to solve is, I want to lazy-load my StripeForm, so that it doesn't load until needed (and therefor maybe never at all).

I have the React.lazy import and my webpack bundle working fine. The issue is "until needed".

As I understand it, lazy-loaded components load when they begin rendering, which I suppose is usually when they move into the viewport. And I equally suppose that "usually" is overridden by the fact that I'm calling .render, which I guess causes it to render immediately.

Those guesses might be wrong, but the fact is, when I load my web page, the supposedly lazy-loaded component is loaded, even if it's not in the viewport.

How to I get these sort of "widgets" (there are several others on the page) to load lazily (i.e., to attach to my root placeholder element, but not actually load and render until necessary)?

CodePudding user response:

You're already using lazy, so React will only import the component if it's not being rendered. The problem is that you're still rendering the component by default, so the component is still being loaded once it's available.

React is declarative, so the way to solve this is to conditionally render the component only when you want it to be rendered. You can implement this by using a visibility library such as react-is-visible, for example:

import React, { useRef } from 'react'
import { useIsVisible } from 'react-is-visible'

function LazyStripeForm() {
  const ref = useRef()
  const isVisible = useIsVisible(ref, { once: true })

  return <div ref={ref}>{isVisible && <StripeForm />}</div>
}

Now you can render LazyStripeForm instead of StripeForm and it should do what you want.

Also, if StripeForm has a lot of dependencies, you should ensure your build tool is code splitting the file so that it's not increasing the size of your main bundle unnecessarily.

  • Related