Skip to content

Instantly share code, notes, and snippets.

@wesleycole
Created September 24, 2021 01:55
Show Gist options
  • Select an option

  • Save wesleycole/04dac6eaf352c0c58b25a2cb096486ae to your computer and use it in GitHub Desktop.

Select an option

Save wesleycole/04dac6eaf352c0c58b25a2cb096486ae to your computer and use it in GitHub Desktop.
Hooks for working with Amplify's DataStore library
function App() {
const { data, isLoading, error, refetch } = useQuery(
async () => await DataStore.query(Model),
);
const { handler, isLoading, isError, isSuccess } = useMutation<Model>(async (variables) => {
await DataStore.save(new Sermon(template));
});
useSubscription(() => DataStore.observe(Model), {
onUpdate: refetch,
})
return (
<button onClick={() => handler({})}>Perform the mutation</button>
)
}
import { DependencyList, useCallback, useState } from 'react';
interface UseMutationOptions {
deps?: DependencyList;
}
type State = 'idle' | 'loading' | 'error' | 'success';
export function useMutation<T>(
fn: any,
{
deps = [],
onSuccess,
onError,
}: UseMutationOptions & {
onSuccess?: () => void;
onError?: (error: any) => void;
} = {},
): {
isLoading: boolean;
isError: boolean;
isSuccess: boolean;
state: State;
error: any;
handler: (args: any) => Promise<T>;
} {
const [state, setState] = useState<State>('idle');
const [error, setError] = useState();
const isLoading = state === 'loading';
const isError = state === 'error';
const isSuccess = state === 'success';
const callback = useCallback((...args: any): any => fn(...args), deps);
async function handler(args: any) {
setState('loading');
try {
const result = await callback(args);
setState('success');
onSuccess?.();
return result;
} catch (err) {
setState('error');
setError(err);
onError?.(error);
}
}
return { isLoading, isError, isSuccess, state, error, handler };
}
import {
DependencyList,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { useIsMounted } from './utils/useIsMounted';
type FunctionReturningPromise = (...args: any[]) => Promise<any>;
type PromiseType<P extends Promise<any>> = P extends Promise<infer T>
? T
: never;
export type AsyncState<T> =
| {
isLoading: boolean;
error?: undefined;
data?: undefined;
}
| {
isLoading: true;
error?: Error | undefined;
data?: T;
}
| {
isLoading: false;
error: Error;
data?: undefined;
}
| {
isLoading: false;
error?: undefined;
data: T;
};
type State<T extends FunctionReturningPromise> = AsyncState<
PromiseType<ReturnType<T>>
>;
export function useQuery<T extends FunctionReturningPromise>(
fn: T,
options: {
deps?: DependencyList;
initialState?: State<T>;
onFetch?: (args: any) => void;
} = {},
) {
const initialState = options?.initialState || { isLoading: false };
const lastCallId = useRef(0);
const isMounted = useIsMounted();
const [state, setState] = useState<State<T>>(initialState);
const callback = useCallback((...args: []): ReturnType<T> => {
const callId = ++lastCallId.current;
setState((prevState) => ({ ...prevState, loading: true }));
return fn(...args).then(
(data) => {
isMounted() &&
callId === lastCallId.current &&
setState({ data, isLoading: false });
options?.onFetch?.(data);
return data;
},
(error) => {
isMounted() &&
callId === lastCallId.current &&
setState({ error, isLoading: false });
return error;
},
) as ReturnType<T>;
}, options?.deps || []);
useEffect(() => {
callback();
}, [callback]);
return { ...state, refetch: callback };
}
import { DependencyList, useCallback, useEffect } from 'react';
enum OpType {
INSERT = 'INSERT',
UPDATE = 'UPDATE',
DELETE = 'DELETE',
}
type SubscriptionMessage = {
opType: OpType;
element: any;
model: any;
condition: any;
};
export interface UseSubscriptionOptions {
deps?: DependencyList;
refetch?: () => void;
onInsert?: () => void;
onUpdate?: () => void;
onDelete?: () => void;
}
export function useSubscription<T>(
fn: any,
{
deps = [],
refetch,
onInsert,
onUpdate,
onDelete,
}: UseSubscriptionOptions = {},
) {
const callback = useCallback((...args: []): any => fn(...args), deps);
useEffect(() => {
const subscription = callback().subscribe((msg: SubscriptionMessage) => {
refetch?.();
if (msg?.opType === 'INSERT') onInsert?.();
if (msg?.opType === 'UPDATE') onUpdate?.();
if (msg?.opType === 'DELETE') onDelete?.();
});
return () => subscription.unsubscribe();
}, [callback]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment