11import { createContext , useCallback , useContext , useEffect , useMemo , useReducer , useRef } from 'react'
22import { deepEqual , deepMerge , generateConcurrentFn } from './utils'
3- import { useValueRef , useEnhancedMemo , useCachedData } from './hooks'
3+ import { useValueRef , useEnhancedMemo , useCachedData , useEnhancedEffect } from './hooks'
44import { ApiResult , Cache , defaultResult , generateCacheKey , isCached } from './cache'
55export { Cache } from './cache'
66
77export type ApiVariables < T extends Partial < Record < 'body' | 'query' | 'body' , any > > = { } > = T
88
99interface ApiConfig < APIs > {
1010 fetcher : ( api : APIs [ keyof APIs ] , variables : ApiVariables ) => Promise < unknown >
11+ refetchOnFocus ?: boolean
12+ useFocused ?: ( ) => boolean
1113}
1214
1315interface ApiContext < APIs > {
@@ -20,13 +22,21 @@ type ApiProviderProps<T> = React.PropsWithChildren<{
2022 config : ApiConfig < T >
2123} >
2224
25+ function useDefaultFocused ( ) {
26+ return true
27+ }
28+
2329function createProvider < T > ( ctx : React . Context < ApiContext < T > > ) {
2430 return function Provider ( {
2531 config,
2632 children,
2733 cache = new Cache ( ) ,
2834 } : ApiProviderProps < T > ) {
29- const { fetcher } = config
35+ const {
36+ fetcher,
37+ refetchOnFocus = true ,
38+ useFocused = useDefaultFocused
39+ } = config
3040 const concurrentFetcher = useMemo ( ( ) => generateConcurrentFn ( fetcher ) , [ fetcher ] )
3141
3242 return (
@@ -35,6 +45,8 @@ function createProvider<T>(ctx: React.Context<ApiContext<T>>) {
3545 cache,
3646 config : {
3747 ...config ,
48+ useFocused,
49+ refetchOnFocus,
3850 fetcher : concurrentFetcher
3951 }
4052 } }
@@ -59,6 +71,7 @@ function partialStateReducer<T extends {}>(s: T, action: Partial<T>) {
5971
6072export interface UseLazyApiOptions < TData , TError , TVariables > {
6173 cached ?: boolean
74+ refetchOnFocus ?: boolean
6275 variables ?: TVariables
6376 onFetch ?: ( ) => Promise < any >
6477 onCompleted ?: ( params : { data : TData | null , error : TError | null } ) => Promise < any >
@@ -76,17 +89,19 @@ function createUseLazyApi<
7689 TApiError extends TError [ K ] ,
7790 TApiVariables extends TVariables [ K ]
7891 > ( key : K , defaultOpts : UseLazyApiOptions < TApiData , TApiError , TApiVariables > = { } ) {
92+ const fetchedRef = useRef ( false )
7993 const defaultOptsRef = useValueRef ( defaultOpts )
8094 const {
8195 cache,
82- config : { fetcher }
96+ config : { fetcher, refetchOnFocus , useFocused }
8397 } = useContext ( ctx )
8498 const [ variables , setVariables ] = useReducer ( stateReducer as typeof stateReducer < TApiVariables > , ( defaultOpts . variables || { } ) as TApiVariables )
8599 const variablesRef = useValueRef ( variables )
86100 const cacheKey = useMemo ( ( ) => generateCacheKey ( key , variables ) , [ key , variables ] )
87101 const cachedResult = useCachedData ( cacheKey , cache ) as ApiResult < TApiData , TApiError >
88102 const [ localResult , setLocalResult ] = useReducer ( partialStateReducer as typeof partialStateReducer < ApiResult < TApiData , TApiError > > , defaultResult )
89103 const prevResultRef = useRef ( { } )
104+ const prevVariablesRef = useRef < TApiVariables > ( )
90105 const cachedRef = useRef ( defaultOpts . cached ?? true )
91106
92107 const fetch = useCallback ( async ( opts : Pick < UseLazyApiOptions < TApiData , TApiError , TApiVariables > , 'variables' > = { } ) => {
@@ -127,7 +142,9 @@ function createUseLazyApi<
127142
128143 if ( onCompleted ) await onCompleted ( { data, error } )
129144
145+ fetchedRef . current = true
130146 prevResultRef . current = { data, error }
147+ prevVariablesRef . current = variables as TApiVariables
131148
132149 if ( cached ) {
133150 cache . set ( cacheKey , {
@@ -146,13 +163,24 @@ function createUseLazyApi<
146163 return { data, error }
147164 } , [ key , cache , fetcher , setVariables , setLocalResult ] )
148165
166+ const refetch = useCallback ( ( ) => fetch ( { variables : prevVariablesRef . current } ) , [ fetch ] )
167+
168+ /**
169+ * automatically refetch on focus
170+ */
171+ const focused = useFocused ! ( )
172+ const refetchOnFocusRef = useRef ( defaultOpts . refetchOnFocus ?? refetchOnFocus )
173+ useEnhancedEffect ( ( ) => {
174+ if ( focused && refetchOnFocusRef . current && fetchedRef . current ) refetch ( )
175+ } , [ focused , refetch ] )
176+
149177 return [ fetch , {
150178 ...( cachedRef . current ? {
151179 ...cachedResult ,
152180 // when we changed variables, the cached result might be empty, but we want to keep showing the previous result
153181 ...( ! isCached ( cacheKey , cache ) && prevResultRef . current ) ,
154182 } : localResult ) ,
155- refetch : fetch
183+ refetch
156184 } ] as const
157185 }
158186}
@@ -172,7 +200,8 @@ function createUseMutationApi<
172200 > ( key : K , opts : UseLazyApiOptions < TApiData , TApiError , TApiVariables > = { } ) {
173201 return useLazyApi < K , TApiData , TApiError , TApiVariables > ( key , {
174202 ...opts ,
175- cached : false
203+ cached : false ,
204+ refetchOnFocus : false
176205 } )
177206 }
178207}
0 commit comments