Skip to content

Commit

Permalink
store key in cache
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Jun 25, 2022
1 parent f5105c5 commit d465c2e
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 48 deletions.
8 changes: 3 additions & 5 deletions _internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import * as revalidateEvents from './constants'
import { defaultConfig } from './utils/config'

type Falsy = null | undefined | false
export type TruthyKey = Exclude<Arguments, Falsy>

export type GlobalState = [
Record<string, RevalidateCallback[]>, // EVENT_REVALIDATORS
Record<string, [number, number]>, // MUTATION: [ts, end_ts]
Record<string, [any, number]>, // FETCH: [data, ts]
Record<string, FetcherResponse<any>>, // PRELOAD
ScopedMutator, // Mutator
(key: string, value: any, prev: any) => void, // Setter
(key: string, callback: (current: any, prev: any) => void) => () => void, // Subscriber
Set<TruthyKey> // Keys
(key: string, callback: (current: any, prev: any) => void) => () => void // Subscriber
]
export type FetcherResponse<Data = unknown> = Data | Promise<Data>
export type BareFetcher<Data = unknown> = (
Expand Down Expand Up @@ -184,6 +180,7 @@ export type State<Data = any, Error = any> = {
error?: Error
isValidating?: boolean
isLoading?: boolean
key?: Key
}

export type MutatorFn<Data = any> = (
Expand Down Expand Up @@ -272,6 +269,7 @@ export type RevalidateCallback = <K extends RevalidateEvent>(
) => RevalidateCallbackReturnType[K]

export interface Cache<Data = any> {
keys(): IterableIterator<Key>
get(key: Key): State<Data> | undefined
set(key: Key, value: State<Data>): void
delete(key: Key): void
Expand Down
3 changes: 1 addition & 2 deletions _internal/utils/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ export const initCache = <Data = any>(
{},
mutate,
setter,
subscribe,
new Set()
subscribe
])
if (!IS_SERVER) {
// When listening to the native events for auto revalidations,
Expand Down
15 changes: 9 additions & 6 deletions _internal/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SWRGlobalState } from './global-state'
import { Key, Cache, State, GlobalState } from '../types'
import type { Key, Cache, State, GlobalState } from '../types'

const EMPTY_CACHE = {}
export const noop = () => {}

// Using noop() as the undefined value as undefined can possibly be replaced
Expand All @@ -18,8 +17,11 @@ export const isFunction = <
>(
v: unknown
): v is T => typeof v == 'function'
export const isEmptyCache = (v: any): boolean => v === EMPTY_CACHE
export const mergeObjects = (a: any, b: any) => OBJECT.assign({}, a, b)
export const isEmptyCacheState = (v: State<any, any>): boolean => {
const keys = Object.keys(v)
return keys.length === 1 && keys[0] === 'key'
}

const STR_UNDEFINED = 'undefined'

Expand All @@ -34,13 +36,14 @@ export const createCacheHelper = <Data = any, T = State<Data, any>>(
key: Key
) => {
const state = SWRGlobalState.get(cache) as GlobalState
const emptyCache = { key }
return [
// Getter
() => (cache.get(key) || EMPTY_CACHE) as T,
() => (cache.get(key) || emptyCache) as T,
// Setter
(info: T) => {
const prev = cache.get(key)
state[5](key as string, mergeObjects(prev, info), prev || EMPTY_CACHE)
const prev = cache.get(key) || emptyCache
state[5](key as string, mergeObjects(prev, info), prev)
},
// Subscriber
state[6]
Expand Down
35 changes: 16 additions & 19 deletions _internal/utils/mutate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import {
GlobalState,
State,
Arguments,
TruthyKey
Key
} from '../types'

type KeyFilter = (key?: TruthyKey) => boolean
type KeyFilter = (key?: Key) => boolean
type MutateState<Data> = State<Data, any> & {
// The previously committed data.
_c?: Data
}

export async function internalMutate<Data>(
cache: Cache,
Expand All @@ -36,6 +40,7 @@ export async function internalMutate<Data>(
]
): Promise<any> {
const [cache, _key, _data, _opts] = args

// When passing as a boolean, it's explicitly used to disable/enable
// revalidation.
const options =
Expand All @@ -49,31 +54,22 @@ export async function internalMutate<Data>(
const revalidate = options.revalidate !== false
const rollbackOnError = options.rollbackOnError !== false

const KEYS = (SWRGlobalState.get(cache) as GlobalState)[6]

// If 2nd arg is key filter, return the mutation results of filtered keys
if (isFunction(_key)) {
const keyFilter = _key
const matchedKeys = []
for (const k of KEYS.values()) {
if (keyFilter(k)) matchedKeys.push(k)
const matchedKeys: Key[] = []
for (const originKey of cache.keys()) {
if (keyFilter(originKey)) matchedKeys.push(originKey)
}
return await Promise.all(matchedKeys.map(mutateByKey))
} else {
const [serializedKey] = serialize(_key)
if (!serializedKey) return
return await mutateByKey(serializedKey)
}
return await mutateByKey(_key)

async function mutateByKey(_k: TruthyKey): Promise<Data | undefined> {
async function mutateByKey(_k: Key): Promise<Data | undefined> {
// Serialize key
const [key] = serialize(_k)
const [get, set] = createCacheHelper<
Data,
State<Data, any> & {
// The previously committed data.
_c?: Data
}
>(cache, key)
if (!key) return
const [get, set] = createCacheHelper<Data, MutateState<Data>>(cache, key)
const [EVENT_REVALIDATORS, MUTATION, FETCH] = SWRGlobalState.get(
cache
) as GlobalState
Expand Down Expand Up @@ -159,6 +155,7 @@ export async function internalMutate<Data>(
set({ data, _c: UNDEFINED })
}
}

// If we should write back the cache after request.
if (populateCache) {
if (!error) {
Expand Down
27 changes: 12 additions & 15 deletions core/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
OBJECT,
isFunction,
createCacheHelper,
isEmptyCache,
isEmptyCacheState,
SWRConfig as ConfigProvider,
withArgs,
subscribeCallback,
Expand Down Expand Up @@ -67,7 +67,7 @@ export const useSWRHandler = <Data = any, Error = any>(
keepPreviousData
} = config

const [EVENT_REVALIDATORS, MUTATION, FETCH, , , , KEYS] = SWRGlobalState.get(
const [EVENT_REVALIDATORS, MUTATION, FETCH] = SWRGlobalState.get(
cache
) as GlobalState

Expand All @@ -87,6 +87,7 @@ export const useSWRHandler = <Data = any, Error = any>(

// Refs to keep the key and config.
const keyRef = useRef(key)
const originKeyRef = useRef(_key)
const fetcherRef = useRef(fetcher)
const configRef = useRef(config)
const getConfig = () => configRef.current
Expand Down Expand Up @@ -117,7 +118,7 @@ export const useSWRHandler = <Data = any, Error = any>(
return true
})()
if (!shouldStartRequest) return snapshot
if (isEmptyCache(snapshot)) {
if (isEmptyCacheState(snapshot)) {
return {
isValidating: true,
isLoading: true
Expand Down Expand Up @@ -206,8 +207,12 @@ export const useSWRHandler = <Data = any, Error = any>(
isInitialMount &&
shouldDoInitialRevalidation
)
const isValidating = isUndefined(cached.isValidating) ? defaultValidatingState : cached.isValidating
const isLoading = isUndefined(cached.isLoading) ? defaultValidatingState : cached.isLoading
const isValidating = isUndefined(cached.isValidating)
? defaultValidatingState
: cached.isValidating
const isLoading = isUndefined(cached.isLoading)
? defaultValidatingState
: cached.isLoading

// The revalidation function is a carefully crafted wrapper of the original
// `fetcher`, to correctly handle the many edge cases.
Expand Down Expand Up @@ -441,11 +446,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// By using `bind` we don't need to modify the size of the rest arguments.
// Due to https://github.com/microsoft/TypeScript/issues/37181, we have to
// cast it to any for now.
internalMutate.bind(
UNDEFINED,
cache,
(k: string) => k === keyRef.current
) as any,
internalMutate.bind(UNDEFINED, cache, keyRef.current) as any,
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
)
Expand All @@ -461,11 +462,6 @@ export const useSWRHandler = <Data = any, Error = any>(
}
})

useIsomorphicLayoutEffect(() => {
const isTruthyKey = _key && key && !isFunction(_key)
if (isTruthyKey && !KEYS.has(_key)) KEYS.add(_key)
}, [key])

// After mounted or key changed.
useIsomorphicLayoutEffect(() => {
if (!key) return
Expand Down Expand Up @@ -501,6 +497,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// Mark the component as mounted and update corresponding refs.
unmountedRef.current = false
keyRef.current = key
originKeyRef.current = _key
initialMountedRef.current = true

// Trigger a revalidation.
Expand Down
2 changes: 1 addition & 1 deletion test/use-swr-local-mutation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ describe('useSWR - local mutation', () => {
globalMutate(() => {
throw e
}, Promise.resolve('data'))
).resolves.toEqual([])
).rejects.toEqual(e)
})

it('should get bound mutate from useSWR', async () => {
Expand Down

0 comments on commit d465c2e

Please sign in to comment.