-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
types: infer provider type as cache type #1506
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,5 +21,6 @@ export { | |
Fetcher, | ||
MutatorCallback, | ||
Middleware, | ||
Arguments | ||
Arguments, | ||
DefaultProvider | ||
} from './types' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
import { createContext, createElement, useContext, useState, FC } from 'react' | ||
import { | ||
createContext, | ||
createElement, | ||
useContext, | ||
useState, | ||
ReactElement | ||
} from 'react' | ||
import { cache as defaultCache } from './config' | ||
import { initCache } from './cache' | ||
import { mergeConfigs } from './merge-config' | ||
|
@@ -8,33 +14,38 @@ import { | |
SWRConfiguration, | ||
FullConfiguration, | ||
ProviderConfiguration, | ||
Cache | ||
Cache, | ||
CacheProvider | ||
} from '../types' | ||
|
||
export const SWRConfigContext = createContext<Partial<FullConfiguration>>({}) | ||
|
||
const SWRConfig: FC<{ | ||
value?: SWRConfiguration & | ||
Partial<ProviderConfiguration> & { | ||
provider?: (cache: Readonly<Cache>) => Cache | ||
} | ||
}> = ({ children, value }) => { | ||
type SWRConfigValue = SWRConfiguration & | ||
Partial<ProviderConfiguration> & { | ||
provider?: CacheProvider<any> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not look correct to me. export type Cache<T = DefaultProvider, Data = any> = T extends ICache<Data>
? T
: never
export type CacheProvider<Data = any> = (cache: Cache<Data>) => Cache<Data>
CacheProvider<any> = (cache: Cache<any>) => Cache<any> = (cache: any) => any We need provider to return a Cache here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't get what do you mean here. Can you rephrase it more specificly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previoustype Provider: (cache: Readonly<Cache>) => Cache
const cache = new Map()
<SWRConfig value={{ provider: () => cache }}>{children}</SWRConfig>
// This will throw type error since { a: foo } does not satisfy the requirement
<SWRConfig value={{ provider: () => {a: foo } }}>{children}</SWRConfig> Currenttype Provider = CacheProvider<any> = (_: any) => any
const cache = new Map()
<SWRConfig value={{ provider: () => cache }}>{children}</SWRConfig>
// This will not throw type error any more
<SWRConfig value={{ provider: () => {a: foo } }}>{children}</SWRConfig> |
||
} | ||
|
||
const SWRConfig = ({ | ||
value, | ||
children | ||
}: { | ||
value?: SWRConfigValue | ||
children?: React.ReactNode | ||
}): ReactElement | null => { | ||
// Extend parent context values and middleware. | ||
const extendedConfig = mergeConfigs(useContext(SWRConfigContext), value) | ||
|
||
const cache = extendedConfig.cache || defaultCache | ||
// Should not use the inherited provider. | ||
const provider = value && value.provider | ||
|
||
// Use a lazy initialized state to create the cache on first access. | ||
const [cacheContext] = useState(() => | ||
provider | ||
? initCache(provider(extendedConfig.cache || defaultCache), value) | ||
: UNDEFINED | ||
provider ? initCache(provider(cache), value) : UNDEFINED | ||
) | ||
|
||
// Override the cache if a new provider is given. | ||
if (cacheContext) { | ||
extendedConfig.cache = cacheContext[0] | ||
extendedConfig.cache = cacheContext[0] as Cache | ||
extendedConfig.mutate = cacheContext[1] | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import React from 'react' | ||
import { useEffect } from 'react' | ||
import { DefaultProvider, SWRConfig, useSWRConfig } from 'swr' | ||
|
||
export function test1() { | ||
function useCacheClear() { | ||
const { cache: defaultCache } = useSWRConfig() | ||
const { cache } = useSWRConfig<DefaultProvider>() | ||
|
||
useEffect(() => { | ||
return () => { | ||
cache.clear() // infer DefaultProvider | ||
defaultCache.clear() // infer DefaultProvider | ||
} | ||
}, []) | ||
return null | ||
} | ||
|
||
return useCacheClear | ||
} | ||
|
||
export function test2() { | ||
const provider2 = new Map([['k1', 'v1']]) | ||
function Inner() { | ||
// infer Map<string, string> | ||
const { cache } = useSWRConfig<typeof provider2>() | ||
|
||
useEffect(() => { | ||
return () => cache.clear() | ||
}, []) | ||
return null | ||
} | ||
|
||
function useCustomCache2() { | ||
return ( | ||
<SWRConfig value={{ provider: () => provider2 }}> | ||
<SWRConfig value={{ provider: (c: typeof provider2) => c }}> | ||
<Inner /> | ||
</SWRConfig> | ||
</SWRConfig> | ||
) | ||
} | ||
|
||
return useCustomCache2 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
string | null
instead of juststring
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because
swr/infinite
has some case that cache key isstring | null
like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That feels like a bug (facepalm)