React Promise Suspense Hook
react-promise-suspense provides a single React hook, `usePromise`, designed to integrate any Promise-based asynchronous operation with React Suspense. It allows components to 'suspend' rendering while awaiting data, leveraging React's built-in loading states via `React.Suspense` boundaries. The library reached its current stable version, 0.3.4, in March 2020, and has not received updates since, indicating it is no longer actively maintained. While it functions similarly to libraries like `fetch-suspense`, `react-promise-suspense` is generic and works with any JavaScript Promise, not just `fetch`. Due to its last update being over six years ago, compatibility with newer React versions (beyond React 16/17) and modern Suspense features like `use` hook or SuspenseList might be limited or require careful testing.
Common errors
-
Error: A component suspended while rendering, but no fallback UI was specified.
cause A component using `usePromise` rendered before its data was available, but it was not wrapped by a `Suspense` boundary.fixEnsure the component that calls `usePromise` (or an ancestor component) is rendered within a `<Suspense fallback={/* your loading UI */}>` component. -
TypeError: (0 , react_promise_suspense__WEBPACK_IMPORTED_MODULE_0__.default) is not a function
cause Incorrect import statement for `usePromise`, or bundler misconfiguration handling default exports.fixUse the correct import syntax: `import usePromise from 'react-promise-suspense';`. Verify your bundler (Webpack, Rollup) is configured to correctly handle ESM default exports. -
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause The `usePromise` hook was called outside of a React function component or a custom hook.fixEnsure `usePromise` is only invoked directly within the top level of a React function component or another custom hook function.
Warnings
- gotcha The `react-promise-suspense` library has not been updated since March 2020. Its compatibility with newer React versions (e.g., React 18 and above) or contemporary Suspense features may be limited or lead to unexpected behavior. Use with caution in modern React projects.
- gotcha As a 0.x.x version package, the API of `react-promise-suspense` was never officially considered stable. Although no explicit breaking changes are documented in its sparse release history, future compatibility or expected behavior cannot be guaranteed.
- gotcha When using `usePromise`, ensure that the component calling it is wrapped in a `React.Suspense` boundary. Failing to do so will result in a runtime error when the promise resolves.
Install
-
npm install react-promise-suspense -
yarn add react-promise-suspense -
pnpm add react-promise-suspense
Imports
- usePromise
import { usePromise } from 'react-promise-suspense';import usePromise from 'react-promise-suspense';
- Suspense
import { Suspense } from 'react'; - usePromise (TypeScript Type)
import { usePromise } from 'react-promise-suspense';import type usePromise from 'react-promise-suspense';
Quickstart
import React, { Suspense } from 'react';
import usePromise from 'react-promise-suspense';
const fetchData = async (id) => {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}/`);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
};
const PokemonDisplay = ({ pokemonId }) => {
const data = usePromise(fetchData, [pokemonId]);
return (
<div className="pokemon-card">
<h2>{data.name}</h2>
<img src={data.sprites.front_default} alt={data.name} />
<p>Height: {data.height}</p>
<p>Weight: {data.weight}</p>
</div>
);
};
const App = () => {
return (
<div className="app-container">
<h1>Pokemon Viewer</h1>
<Suspense fallback={<div>Loading Pokemon...</div>}>
<PokemonDisplay pokemonId={1} />
<PokemonDisplay pokemonId={4} />
<PokemonDisplay pokemonId={7} />
</Suspense>
<style>{`
.app-container { font-family: sans-serif; text-align: center; }
.pokemon-card { border: 1px solid #eee; margin: 10px; padding: 15px; border-radius: 8px; display: inline-block; width: 200px; vertical-align: top; }
img { width: 100px; height: 100px; }
`}</style>
</div>
);
};
export default App;