“There is no terror in the bang, only in the anticipation of it.”
— Alfred Hitchcock
Hitchcock is a debugging tool for React Suspense. It wraps your calls to React.lazy()
, provides a simple cache (based on react-cache) and let you pause, delay or invalidate your promises.
Use this only for experimenting with the new React Concurrent Mode. Hitchcock is inefficient and unstable. Also, I have no idea what I'm doing.
- Movies: A clone of @karl clone of @dan_abramov demo
- Suspensify: A clone of the suspense demo from Jared Palmer's React Conf talk.
The code is in the examples folder.
Try it on CodeSandbox
Add the dependency:
$ yarn add hitchcock
Import the Director
component and add it somewhere in your app:
import { Director } from "hitchcock";
function YourApp() {
return (
<Director>
<YourStuff />
</Director>
);
}
Instead of using React.lazy
import lazy
from hitchcock:
import { lazy } from "hitchcock";
const HomePage = lazy(() => import("./components/HomePage"));
// Hitchcock's lazy accepts a second parameter with the name of the component:
const ArtistPage = lazy(() => import("./components/ArtistPage"), "ArtistPage");
// it's optional, but recommended, it isn't always easy to guess the name from the import
import { createResource } from "hitchcock";
const BeerResource = createResource(
id =>
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(r => r.json())
.then(d => d[0]),
id => `beer-${id}`
);
function Beer({ beerId }) {
const beer = BeerResource.read(beerId);
return (
<>
<h1>{beer.name}</h1>
<p>{beer.description}</p>
</>
);
}
createResource
has two parameters. The first one is a function that returns a promise. The second one is a function that returns an id, that id is used as the key in the cache and also is used as the name of the resource in the debugger.
It returns a resource
with a read
method that will suspend a component until the resource is ready (when the promise resolves).
React docs warn about using Suspense as a way to start fetching data when a component renders. The recommended approach is to start fetching before rendering, for example, in an event handler. Hitchcock doesn't solve this problem, but it provides a preload
method if you want to try:
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { createResource, Director } from "hitchcock";
const BeerResource = createResource(
id =>
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(r => r.json())
.then(d => d[0]),
id => `beer-${id}`
);
function App() {
const [beerId, setBeerId] = React.useState(0);
const deferredBeerId = React.useDeferredValue(beerId, { timeoutMs: 1000 });
const showBeer = deferredBeerId > 0;
const handleChange = e => {
const newBeerId = +e.target.value;
BeerResource.preload(newBeerId);
setBeerId(newBeerId);
};
return (
<Director>
Beer # <input type="number" value={beerId} onChange={handleChange} />
<Suspense fallback={<div>{`Loading beer #${beerId}...`}</div>}>
{showBeer && <Beer beerId={deferredBeerId} />}
</Suspense>
</Director>
);
}
function Beer({ beerId }) {
const beer = BeerResource.read(beerId);
return (
<>
<h1>{beer.name}</h1>
<p>{beer.description}</p>
</>
);
}
const container = document.getElementById("root");
ReactDOM.createRoot(container).render(<App />);
$ git clone git@github.com:pomber/hitchcock.git
$ cd hitchcock
$ npx lerna bootstrap
Run the examples:
$ yarn start:example movies
$ yarn start:example suspensify
Publish new version:
$ yarn build:packages
$ npx lerna publish
Released under MIT license.