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.
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
Basic namespaced (BEM) css can be imported:import '@unleashit/async-handler/dist/async-handler.css'
. CSS Module support is baked in. If you use CSS Modules you can import '@unleashit/async-handler/dist/async-handler.module.css'
or import your own custom module targeting the internal classes and pass to the cssModule
prop. Please see CSS in the main readme of the repo for more info.API and Props
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;
}
| Name | Type | Description | default | | ------------------ | -------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------ | | request | () => Promise | Called if cache function exists and doesn't return a falsy value | required | | cache | () => object | any | false | null | Optional function that should return a cache object or null (calls the request) | n/a | | noResultsComponent | () => React.ReactNode | React component to override default no results message | Nothing found. | | errorComponent | ({ error }: {error: any} ) => React.ReactNode | React component to override default error message | default message with error displayed | | loaderComponent | () => React.ReactNode | React component to override the default loader | Loading... | | cssModule | { key: string: string } | CSS Module object that optionally replaces default. Class names need to match expected names. | undefined | | children | (data: any) => any; | Function to be called with data if request returns with results (AsyncHandler only) | n/a |