Skip to content

Instantly share code, notes, and snippets.

@shannonmoeller
Last active September 19, 2020 17:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shannonmoeller/c3396911dcfa11cd98b65d37e19719af to your computer and use it in GitHub Desktop.
Save shannonmoeller/c3396911dcfa11cd98b65d37e19719af to your computer and use it in GitHub Desktop.
import { useAsync } from './use-async.js';
import { useAsyncCallback } from './use-async-callback.js';
export function Cat() {
const { data, error, loading } = useAsync(
async (signal) => {
const cats = await fetch(
'https://api.thecatapi.com/v1/images/search?size=full',
{ signal }
);
return cats?.[0]?.url;
}
);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Unable to load a cat: {error.message}</p>;
}
if (!data) {
return <p>No cat for you.</p>;
}
return <img src={data} alt="cat" />;
}
export function ResizeCat() {
const [{ data, error, loading }, getCat] = useAsync(
(signal) => async (size) => {
const cats = await fetch(
`https://api.thecatapi.com/v1/images/search?size=${size}`,
{ signal }
);
return cats?.[0]?.url;
}
);
return (
<>
<button onClick={() => getCat('small')}>Small</button>
<button onClick={() => getCat('med')}>Medium</button>
<button onClick={() => getCat('full')}>Full</button>
(loading ? (
<p>Loading...</p>
) : error ? (
<p>Unable to load a cat: {error.message}</p>
) : data ? (
<p>No cat for you.</p>
) : (
<img src={data} alt="cat" />
))
</>
);
}
import { useCallback, useState } from 'react';
export function useAsyncCallback(handlerFactory, deps = []) {
const [state, setState] = useState({
controller: null,
data: null,
error: null,
loading: false,
});
const callback = useCallback(
async (...args) => {
const controller = new AbortController();
const { signal } = controller;
try {
setState(prev => {
if (prev.controller) {
prev.controller.abort();
}
return {
controller,
data: null,
error: null,
loading: true,
};
});
const handler = await handlerFactory(signal);
if (signal.aborted) {
return;
}
const result = await handler(...args);
if (signal.aborted) {
return;
}
setState(prev => ({
...prev,
data: result,
error: null,
loading: false,
}));
} catch (err) {
if (signal.aborted) {
return;
}
controller.abort();
setState(prev => ({
...prev,
data: null,
error: err,
loading: false,
}));
}
},
deps
);
return [state, callback];
}
import { useEffect, useState } from 'react';
export function useAsync(handler, deps = []) {
const [state, setState] = useState({
controller: null,
data: null,
error: null,
loading: true,
});
useEffect(
() => {
const controller = new AbortController();
const { signal } = controller;
(async () => {
try {
setState(prev => {
if (prev.controller) {
prev.controller.abort();
}
return {
controller,
data: null,
error: null,
loading: true,
};
});
const result = await handler(signal);
if (signal.aborted) {
return;
}
setState(prev => ({
...prev,
data: result,
error: null,
loading: false,
}));
} catch (err) {
if (signal.aborted) {
return;
}
controller.abort();
setState(prev => ({
...prev,
data: null,
error: err,
loading: false,
}));
}
})();
return () => {
controller.abort();
};
},
deps
);
return state;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment