@silyze/react-loadable
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

React Loadable

A small utility library for representing and consuming asynchronous values in React applications using the new use hook and server-rendering APIs. It enables you to wrap promises or values into a consistent "loadable" type, extract state for rendering logic, and seamlessly suspend or read values in both client and server environments.


Features

  • loadable: Wrap a promise, value, or undefined into a Loadable<T>.
  • extractState: Inspect the current state of a Loadable<T> (pending, loading, or ready).
  • useLoadable: A React hook that leverages use for suspense or optional immediate reads.
  • synchronize: Read a ready Loadable<T> synchronously or apply a mapping function when it’s available.

Installation

Install from npm:

npm install @silyze/react-loadable

Quickstart

import { loadable, useLoadable } from "@silyze/react-loadable";
import React, { useSyncExternalStore } from "react";

// Wrap an async fetch call into a Loadable
function useData<T>(url: string) {
  function fetcher() {
    return fetch(url).then((res) => res.json() as Promise<T>);
  }
  return loadable(useSyncExternalStore(subscribe, fetcher, fetcher));
}

// In a server-rendered or suspense-enabled component
function MyComponent() {
  const data = useLoadable(useData<{ message: string }>("/api/msg"));

  if (!data) {
    return <div>Loading…</div>;
  }

  return <div>{data.message}</div>;
}

API Reference

LoadingSymbol

A unique Symbol used as the key to store a pending promise inside a loadable object.

const LoadingSymbol: unique symbol;
export type LoadingSymbol = typeof LoadingSymbol;

Loadable<T>

Represents a value that may be loading. Either a direct T, or an object containing a promise under the LoadingSymbol key, or null for pending.

export type Loadable<T> = T | { [LoadingSymbol]: Promise<T> | null };

loadable<T>(promiseOrT?: Promise<T> | T): Loadable<T>

Wraps a promise, value, or undefined into a Loadable<T>:

  • If given a Promise<T>, returns { [LoadingSymbol]: promise }.
  • If given undefined, returns { [LoadingSymbol]: null } (pending).
  • Otherwise, returns the raw value T.
function loadable<T>(promiseOrT: Promise<T> | T | undefined): Loadable<T>;

LoadableState<T>

An enumeration of loadable states:

  • { status: "pending" } — promise is not yet created.
  • { status: "loading" } — promise is active (not resolved).
  • { status: "ok"; value: T } — value is ready.
export type LoadableState<T> =
  | { status: "pending" }
  | { status: "loading" }
  | { status: "ok"; value: T };

extractState<T>(loadable: Loadable<T>): LoadableState<T>

Inspect a Loadable<T> and return its state:

function extractState<T>(loadable: Loadable<T>): LoadableState<T>;

Example:

const state = extractState(loadable(Promise.resolve(42)));
// → { status: "loading" }

useLoadable<T>(loadable: Loadable<T>, wait?: boolean): T | undefined

A React hook for consuming loadables:

  • If the loadable is ready, returns the value.
  • If pending ([LoadingSymbol]: null), returns undefined.
  • If loading and wait is true (default), suspends via use(promise).
  • If loading and wait is false, returns undefined immediately.
function useLoadable<T>(loadable: Loadable<T>, wait?: boolean): T | undefined;

synchronize<T, R?>(loadable: Loadable<T>, onValue?: (value: T) => R): T | R | undefined

Synchronously read a Loadable<T> without React hooks:

  • If not loaded, returns undefined.
  • If loaded and onValue is omitted, returns the raw T.
  • If loaded and onValue is provided, returns the mapped R.
function synchronize<T>(loadable: Loadable<T>): T | undefined;
function synchronize<T, R>(
  loadable: Loadable<T>,
  onValue: (value: T) => R
): R | undefined;

Examples

Extracting State

import { loadable, extractState } from "@silyze/react-loadable";

const l1 = loadable(Promise.resolve(100));
console.log(extractState(l1)); // { status: "loading" }

const l2 = loadable(42);
console.log(extractState(l2)); // { status: "ok", value: 42 }

Controlled Rendering with extractState

import React from "react";
import { loadable, extractState } from "@silyze/react-loadable";

function StatusDisplay(loadableValue) {
  const state = extractState(loadableValue);
  switch (state.status) {
    case "pending":
      return <div>Pending…</div>;
    case "loading":
      return <div>Loading…</div>;
    case "ok":
      return <div>Value: {state.value}</div>;
  }
}

Readme

Keywords

none

Package Sidebar

Install

npm i @silyze/react-loadable

Weekly Downloads

2

Version

1.0.0

License

MIT

Unpacked Size

53.6 kB

Total Files

13

Last publish

Collaborators

  • simeonmarkoski
  • mojsoski