Skip to main content

Async Handler

HOC that takes an async function and returns views for loading, no-data and error states. It accepts an optional method to check a cache before the async function is run.

Deprecated

This package has been deprecated in favor of more complete solutions like React Query.

Demo

https://npm-library-demo.vercel.app/async-handler

Install

npm install @unleashit/async-handler

Required peer dependencies: react.

Example with render prop

import React from 'react';
import AsyncHandler from '@unleashit/async-handler';

class ColorList extends React.Component {
request() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(['red', 'green', 'blue', 'yellow', 'orange', 'black', 'white']);
}, 1500);
});
}

render() {
return (
<AsyncHandler request={this.request}>
{(data) => <div>{data.join(', ')}</div>}
</AsyncHandler>
);
}
}

export default ColorList;

This will display default messages for loading, error or no results* as needed. Note that request should return a promise with just the data part of the response so AsyncHandler can know when to display the no results component.

* no results meaning when an object with no keys or a zero length array is returned.

HOC example using cache and optional components

import { withAsyncHandler } from '@unleashit/async-handler';
import MySpinner from './spinner';

// data to fetch asynchronously, here for demo reasons
const users = [
{
id: 1,
name: 'joe',
age: 30,
},
{
id: 2,
name: 'judy',
age: 27,
},
];

// for demonstration, normally you might use
// Redux or another decoupled place to store the cache.
let userCache = null;

const UserList = ({ data }) => {
return (
<ul>
{data.map((item) => (
<li key={item.id}>
{item.name} is {item.age} years old.
</li>
))}
</ul>
);
};

export default withAsyncHandler({
request: () => {
return new Promise((resolve) => {
setTimeout(() => {
userCache = { users, cacheDate: new Date() };
resolve(users);
}, 1500);
});
},
cache: () => {
return userCache && new Date() - userCache.cacheDate <= 5 * 1000
? userCache.users
: null;
},
loaderComponent: <MySpinner foo={'bar'} />,
noResultsComponent: <div>No user{"'"}s found.</div>,
errorComponent: ({ error }) => (
<div>Oops, there was a problem: {JSON.stringify(error)}</div>
),
})(UserList);

CSS

By default, all components come with basic css styling in two formats: standard (BEM namespaced) and a CSS Module friendly version.

To use the standard version, import it like: import '@unleashit/async-handler/dist/async-handler.css'. Or if you are using CSS Modules you can import css from '@unleashit/async-handler/dist/async-handler.module.css' and provide to the cssModule prop and/or use your own custom module targeting the internal class names.

See styling-and-theming for more info.

API

interface DefaultComponentProps {
cssModule?: {
[key: string]: string;
};
error?: any;
}
type DefaultComponent = (props?: DefaultComponentProps) => React.ReactNode;
interface Props {
request: () => Promise<any>;
cache: () => object | any[] | false | null;
loaderComponent: DefaultComponent;
noResultsComponent: DefaultComponent;
errorComponent: DefaultComponent;
cssModule?: { [key: string]: string };
children: (data: any) => any;
}
NameTypeDescriptiondefault
request() => Promise<any>Called if cache function exists and doesn't return a falsy valuerequired
cache() => object | any[] | false | nullOptional function that should return a cache object or null (calls the request)n/a
noResultsComponent() => React.ReactNodeReact component to override default no results messageNothing found.
errorComponent({ error }: {error: any} ) => React.ReactNodeReact component to override default error messagedefault message with error displayed
loaderComponent() => React.ReactNodeReact component to override the default loaderLoading...
cssModuleRecrord<string, string>CSS Module object that optionally replaces default. Class names need to match expected names.undefined
children(data: any) => anyFunction to be called with data if request returns with results (AsyncHandler only)n/a