import { doRetry , GQtyClient , GQtyError , RetryOptions } from 'gqty' ; import * as React from 'react' ; import { FetchPolicy , fetchPolicyDefaultResolveOptions , OnErrorHandler , useDeferDispatch , useSuspensePromise , } from '../common' ; import type { ReactClientOptionsWithDefaults } from '../utils' ; export type LazyFetchPolicy = Exclude < FetchPolicy , 'cache-first' > ; export interface UseLazyQueryOptions < TData > { onCompleted ? : ( data : TData ) => void ; onError ? : OnErrorHandler ; fetchPolicy ? : LazyFetchPolicy ; retry ? : RetryOptions ; suspense ? : boolean ; } export interface UseLazyQueryState < TData > { data : TData | undefined ; error ? : GQtyError ; isLoading : boolean ; isCalled : boolean ; } type UseLazyQueryReducerAction < TData > = { type : 'cache-found' ; data : TData ; } | { type : 'success' ; data : TData ; } | { type : 'failure' ; error : GQtyError ; } | { type : 'loading' ; } ; function UseLazyQueryReducer < TData > ( state , action ) { switch ( action . type ) { case 'loading' : { if ( state . isLoading ) return state ; return { data : state . data , isLoading : true , isCalled : true , } ; } case 'success' : { return { data : action . data , isLoading : false , isCalled : true , } ; } case 'failure' : { return { data : state . data , isLoading : false , error : action . error , isCalled : true , } ; } case 'cache-found' : { return { data : action . data , isLoading : state . isLoading , isCalled : true , } ; } } } function InitUseLazyQueryReducer < TData > ( ) { return { data : undefined , isLoading : false , isCalled : false , } ; } export interface UseLazyQuery < GeneratedSchema extends { query : object ; } > { < TData = unknown , TArgs = undefined > ( queryFn , options ? ) : readonly [ ( ... opts : undefined extends TArgs ? [ { fn ? : ( query : GeneratedSchema [ 'query' ] , args : TArgs ) => TData ; args ? : TArgs ; fetchPolicy ? : LazyFetchPolicy ; } ? ] : [ { fn ? : ( query : GeneratedSchema [ 'query' ] , args : TArgs ) => TData ; args : TArgs ; fetchPolicy ? : LazyFetchPolicy ; } ] ) => Promise < TData > , UseLazyQueryState < TData > ] ; } export function createUseLazyQuery < GeneratedSchema extends { query : object ; mutation : object ; subscription : object ; } > ( client , { defaults : { retry : defaultRetry , lazyQuerySuspense : defaultSuspense , lazyFetchPolicy : defaultFetchPolicy , } , } ) { const { resolved } = client ; const clientQuery = client . query ; const useLazyQuery = function useLazyQuery < TData , TArgs = undefined > ( fn , opts = { } ) : readonly [ ( callbackArgs ? : { fn ? : ( query : GeneratedSchema [ 'query' ] , args : TArgs ) => TData ; args ? : TArgs ; } ) => Promise < TData > , UseLazyQueryState < TData > ] { const [ state , dispatchReducer ] = React . useReducer ( UseLazyQueryReducer , undefined , InitUseLazyQueryReducer ) as [ UseLazyQueryState < TData > , React . Dispatch < UseLazyQueryReducerAction < TData > > ] ; const dispatch = useDeferDispatch ( dispatchReducer ) ; const stateRef = React . useRef ( state ) ; stateRef . current = state ; const fnRef = React . useRef ( fn ) ; fnRef . current = fn ; const optsRef = React . useRef ( opts ) ; optsRef . current = Object . assign ( { } , opts ) ; optsRef . current . suspense ??= defaultSuspense ; const setSuspensePromise = useSuspensePromise ( optsRef ) ; const queryFn = React . useCallback ( function callback ( callbackArgs = { } ) { dispatch ( { type : 'loading' , } ) ; const { fn : fnArg , args , fetchPolicy = optsRef . current . fetchPolicy ?? defaultFetchPolicy , } = callbackArgs ; const refFn = fnRef . current ; const functionResolve = fnArg ? ( ) => fnArg ( clientQuery , args ) : refFn ? ( ) => refFn ( clientQuery , args ) : ( ( ) => { throw new GQtyError ( 'You have to specify a function to be resolved' , { caller : callback , } ) ; } ) ( ) ; const resolveOptions = fetchPolicyDefaultResolveOptions ( fetchPolicy ) ; return resolved < TData > ( functionResolve , { ... resolveOptions , onCacheData ( data ) { switch ( fetchPolicy ) { case 'cache-and-network' : { dispatch ( { type : 'cache-found' , data , } ) ; stateRef . current . data = data ; return true ; } default : return false ; } } , } ) . then ( ( data ) => { optsRef . current . onCompleted ?. ( data ) ; dispatch ( { type : 'success' , data , } ) ; return data ; } , ( err ) => { const error = GQtyError . create ( err , useLazyQuery ) ; optsRef . current . onError ?. ( error ) ; dispatch ( { type : 'failure' , error , } ) ; throw error ; } ) ; } , [ fnRef , dispatch , optsRef ] ) ; const { retry = defaultRetry } = opts ; return React . useMemo ( ( ) => { const fn = retry ? ( ... args ) => { const promise = queryFn ( ... args ) . catch ( ( err ) => { doRetry ( retry , { onRetry : ( ) => { const promise = queryFn ( ... args ) . then ( ( ) => { } ) ; setSuspensePromise ( promise ) ; return promise ; } , } ) ; throw err ; } ) ; setSuspensePromise ( promise ) ; return promise ; } : ( ... args ) => { const promise = queryFn ( ... args ) ; setSuspensePromise ( promise ) ; return promise ; } ; return [ fn , state ] ; } , [ state , queryFn , retry , optsRef , setSuspensePromise ] ) ; } ; return useLazyQuery ; }