npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@figliolia/react-lazy

v1.1.0

Published

Lazy components that dynamically load on mount

Downloads

636

Readme

React Lazy

Lazy components that support preloading and dynamically load on mount.

Installation

npm i -S @figliolia/react-lazy
#or
yarn add @figliolia/react-lazy

Basic Usage

import type { ErrorInfo } from "react";
import { CreateLazyComponent } from "@figliolia/react-lazy";

const MyLazyComponent = CreateLazyComponent({
  fallback: <div>Loading</div>, 
  errorBoundary: <div>Whoops!</div>,
  loader: () => import("./path/to/Component"),
  onError: (error: Error, errorInfo: ErrorInfo) => {}
});

// Optionally Preload your component using:
void MyLazyComponent.preload()

// Render your component without preloading and it'll
// load on mount
export const MyApp () => {
  return (
    <main>
      <MyLazyComponent {...lazyComponentProps} />
      {/* your other component markup */}
    </main>
  );
}

CreateLazyComponent()

Parameters

loader: An async function that resolves to { default: ComponentType<T> }. This function is passed to React.lazy().

fallback: (optional) A ReactNode to display while to the lazy component is suspended

errorBoundary: (optional) A ReactNode to display if the lazy component throws an error

onError: (optional) A callback to execute if the lazy component throws an error.

Returns

A LazyComponent instance

Advanced Usage - Preloading

This feature is an additional optimization that allows you to optimistically load dynamic components ahead of when they're actually needed.

Consider a case where you have a user logging into your application for the first time. When they press your login button, you'll probably send a request to your server to validate the user's credentials - then, if they are valid, you'll redirect the user into your app.

While the credentials are being sent to the server, it may also be prudent to securely preload some of the components that are sitting behind your authentication. A working example may look something like the following:

import { useRef, useCallback } from "react";
import { LazyHeader, LazyContent, LazyFooter } from "./your-app-components";

export const LoginScreen = () => {
  const preloaded = useRef(false);

  const preloadComponents = useCallback(async () => {
    if(preloaded.current) {
      return;
    }
    try {
      await Promise.all([
        LazyHeader.preload(),
        LazyContent.preload(),
        LazyFooter.preload(),
      ]);
      preloaded.current = true;
    } catch() {
      // silence
    }
  }, [preloaded])

  const onSubmit = useCallback(async (e) => {
    e.preventDefault();
    void preloadComponents();
    try {
      await fetch("/api/auth", {
        method: "POST",
        body: JSON.stringify(/* Login Credentials */)
      });
      redirect("to/your/app");
    } catch(e) {
      // handle error
    }
  }, [preloadComponents]);

  return (
    <form onSubmit={onSubmit}>
      {/* login inputs */}
      <button type="submit" value="Login" />
    </form>
  );
}

Using this technique, we can utilize the time that an API request is already in-flight to cache component assets in the browser. This way when authentication completes the redirect to our main application content is instantaneous.

Advanced Usage - Priority Queue

For very large applications, it's expected that many components will be lazy loaded. For instances such as these, it may make sense to yield the main thread back to the end user and pause loading JavaScript temporarily.

This pattern has been in use at companies like Facebook for several years. Check out Facebook's blog post on how they handle large numbers of asynchronous tasks.

This library comes with a solution for cases such as these:

import { PriorityQueue, LazyComponentFactory } from "@figliolia/react-lazy"

export const LoadingQueue = new PriorityQueue(
  5 /* milliseconds to yield to the main thread */
);

export const CreateLazyComponent = LazyComponentFactory(LoadingQueue);

The LoadingQueue in the example above is a priority queue that'll handle loading lazy components created using CreateLazyComponent(). The LoadingQueue will detect when a user is attempting to interact with the page and pause loading for a specified number of milliseconds to allow events to dispatch on the main thread.

In addition to user-input awareness, the CreateLazyComponent function generated by LazyComponentFactory now comes with the ability to prioritize loading certain components against others using a PriorityLevel:

import { PriorityQueue, LazyComponentFactory, PriorityLevel } from "@figliolia/react-lazy"

export const LoadingQueue = new PriorityQueue(
  5 /* milliseconds to yield to the main thread */
);
export const CreateLazyComponent = LazyComponentFactory(LoadingQueue);

const MyHighPriorityComponent = CreateLazyComponent({
  priority: PriorityLevel.Immediate,
  loader: () => import("./HighPriorityComponent")
});

const MyLowPriorityComponent = CreateLazyComponent({
  priority: PriorityLevel.Background,
  loader: () => import("./LowPriorityComponent")
});

By default Priority.Immediate components will be loaded ahead of Priority.Background components.

A good use of Priority.Immediate would be for components that are core to your UI's most basic functionality, such as headers and sidebars. Similarly, for components that are conditionally used such as modals or drawer menus Priority.Background is more appropriate.

Utilitizing this pattern in large applications can have a great effect on your core metrics such as FMP and TTI.

In addition to loading components, the LoadingQueue in the example above can also be used for just about any asynchronous task. You can use it to load a large javascript library in the background or even preload images that might be below the fold:

import { PriorityQueue, PriorityLevel } from "@figliolia/react-lazy";

export const LoadingQueue = new PriorityQueue();

LoadingQueue.enqueue(
  PriorityLevel.Background,
  () => import("expensive-node-module").then(() => {
    // do something with your expensive module when
    // it loads
  })
)

LoadingQueue.enqueue(
  PriorityLevel.Background,
  () => new Promise(resolve => {
    const image = new Image();
    image.onload = resolve;
    image.onerror = resolve;
    image.src = "path/to/large-image.jpeg";
  })
)

Your expensive JS module and large image will now load optimistically, but behind any Components or tasks with Priority.Immediate.