Created
September 24, 2021 01:55
-
-
Save wesleycole/04dac6eaf352c0c58b25a2cb096486ae to your computer and use it in GitHub Desktop.
Hooks for working with Amplify's DataStore library
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 characters
| 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 characters
| 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 characters
| 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 characters
| 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