Created
September 24, 2021 01:55
-
-
Save wesleycole/04dac6eaf352c0c58b25a2cb096486ae to your computer and use it in GitHub Desktop.
Revisions
-
wesleycole created this gist
Sep 24, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,17 @@ 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> ) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,53 @@ 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 }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,83 @@ 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 }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,47 @@ 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]); }