|
| 1 | +import { useEffect } from 'react'; |
| 2 | +import { |
| 3 | + useSyncState, |
| 4 | + useEventEffect, |
| 5 | + type ComponentEvent, |
| 6 | + type ReactiumSyncState, |
| 7 | +} from '@atomic-reactor/reactium-sdk-core'; |
| 8 | +import { |
| 9 | + useLazyQuery, |
| 10 | + type OperationVariables, |
| 11 | + type DocumentNode, |
| 12 | + type TypedDocumentNode, |
| 13 | + type LazyQueryHookOptions, |
| 14 | + type QueryResult, |
| 15 | + type NoInfer, |
| 16 | +} from '@apollo/client'; |
| 17 | + |
| 18 | +/** |
| 19 | + * @description useSyncGQLQuery is a hook that wraps the useLazyQuery hook from Apollo Client, and adds state management and event handling. |
| 20 | + * @param {DocumentNode | TypedDocumentNode<TData, TVariables>} query The GraphQL query document. |
| 21 | + * @param {LazyQueryHookOptions<NoInfer<TData>, NoInfer<TVariables>>} options Options to be passed to the useLazyQuery hook. |
| 22 | + * @param {'set' | 'change' | 'loaded' | 'refreshed' | string} [updateEvent='set'] The event name that triggers a rerender |
| 23 | + * @return {ReactiumSyncState<QueryResult<TData,TVariables>>} The sync state object. |
| 24 | + * @see {@link [useLazyQuery](https://www.apollographql.com/docs/react/api/react/hooks/#uselazyquery)} for more information on the useLazyQuery hook from which this hook is derived. |
| 25 | + * @see {@link [ReactiumSyncState](https://reactiumcore.github.io/reactium-sdk-core/classes/ReactiumSyncState.html)} to understand the underlying synchronized state object. |
| 26 | + */ |
| 27 | +export const useSyncGQLQuery = < |
| 28 | + TData = any, |
| 29 | + TVariables extends OperationVariables = OperationVariables, |
| 30 | +>( |
| 31 | + query: DocumentNode | TypedDocumentNode<TData, TVariables>, |
| 32 | + options?: LazyQueryHookOptions<NoInfer<TData>, NoInfer<TVariables>>, |
| 33 | + updateEvent: 'set' | 'change' | 'loaded' | 'refreshed' | string = 'set', |
| 34 | +): ReactiumSyncState<QueryResult<TData, TVariables>> => { |
| 35 | + const [lazy, result] = useLazyQuery(query, options); |
| 36 | + const state = useSyncState<typeof result>(result, updateEvent); |
| 37 | + |
| 38 | + state.extend( |
| 39 | + 'refresh', |
| 40 | + async (variables?: Partial<TVariables>): Promise<void> => { |
| 41 | + const vars = variables || options?.variables; |
| 42 | + state.dispatch('refresh'); |
| 43 | + const newResponse = await result.refetch(vars); |
| 44 | + state.set(newResponse, undefined); |
| 45 | + state.dispatch<typeof newResponse>('refreshed', newResponse); |
| 46 | + }, |
| 47 | + ); |
| 48 | + |
| 49 | + useEventEffect(state, { |
| 50 | + load: async (e): Promise<void> => { |
| 51 | + const { detail } = e as ComponentEvent<typeof options>; |
| 52 | + state.set('loading', true); |
| 53 | + const result = await lazy(detail); |
| 54 | + state.set(result, undefined); |
| 55 | + state.dispatch('loaded', result); |
| 56 | + }, |
| 57 | + |
| 58 | + refreshed: (e): void => { |
| 59 | + const { detail } = e as ComponentEvent<typeof result>; |
| 60 | + state.set(detail, undefined); |
| 61 | + }, |
| 62 | + }); |
| 63 | + |
| 64 | + useEffect((): void => { |
| 65 | + if (result.called) return; |
| 66 | + state.dispatch('load', options); |
| 67 | + }, []); |
| 68 | + |
| 69 | + return state; |
| 70 | +}; |
0 commit comments