-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
cache.ts
136 lines (125 loc) · 4.29 KB
/
cache.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { defaultConfigOptions } from './web-preset'
import { IS_SERVER } from './env'
import { UNDEFINED, mergeObjects, noop } from './helper'
import { internalMutate } from './mutate'
import { SWRGlobalState } from './global-state'
import * as revalidateEvents from '../constants'
import {
Cache,
ScopedMutator,
RevalidateEvent,
RevalidateCallback,
ProviderConfiguration,
GlobalState
} from '../types'
const revalidateAllKeys = (
revalidators: Record<string, RevalidateCallback[]>,
type: RevalidateEvent
) => {
for (const key in revalidators) {
if (revalidators[key][0]) revalidators[key][0](type)
}
}
export const initCache = <Data = any>(
provider: Cache<Data>,
options?: Partial<ProviderConfiguration>
):
| [Cache<Data>, ScopedMutator<Data>, () => void, () => void]
| [Cache<Data>, ScopedMutator<Data>]
| undefined => {
// The global state for a specific provider will be used to deduplicate
// requests and store listeners. As well as a mutate function that bound to
// the cache.
// Provider's global state might be already initialized. Let's try to get the
// global state associated with the provider first.
if (!SWRGlobalState.has(provider)) {
const opts = mergeObjects(defaultConfigOptions, options)
// If there's no global state bound to the provider, create a new one with the
// new mutate function.
const EVENT_REVALIDATORS = {}
const mutate = internalMutate.bind(
UNDEFINED,
provider
) as ScopedMutator<Data>
let unmount = noop
const subscriptions: Record<string, ((current: any, prev: any) => void)[]> =
{}
const subscribe = (
key: string,
callback: (current: any, prev: any) => void
) => {
const subs = subscriptions[key] || []
subscriptions[key] = subs
subs.push(callback)
return () => {
subs.splice(subs.indexOf(callback), 1)
}
}
const setter = (key: string, value: any, prev: any) => {
provider.set(key, value)
const subs = subscriptions[key]
if (subs) {
for (let i = subs.length; i--; ) {
subs[i](value, prev)
}
}
}
const initProvider = () => {
if (!SWRGlobalState.has(provider)) {
// Update the state if it's new, or the provider has been extended.
SWRGlobalState.set(provider, [
EVENT_REVALIDATORS,
{},
{},
mutate,
setter,
subscribe
])
if (!IS_SERVER) {
// When listening to the native events for auto revalidations,
// we intentionally put a delay (setTimeout) here to make sure they are
// fired after immediate JavaScript executions, which can possibly be
// React's state updates.
// This avoids some unnecessary revalidations such as
// https://github.com/vercel/swr/issues/1680.
const releaseFocus = opts.initFocus(
setTimeout.bind(
UNDEFINED,
revalidateAllKeys.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.FOCUS_EVENT
)
)
)
const releaseReconnect = opts.initReconnect(
setTimeout.bind(
UNDEFINED,
revalidateAllKeys.bind(
UNDEFINED,
EVENT_REVALIDATORS,
revalidateEvents.RECONNECT_EVENT
)
)
)
unmount = () => {
releaseFocus && releaseFocus()
releaseReconnect && releaseReconnect()
// When un-mounting, we need to remove the cache provider from the state
// storage too because it's a side-effect. Otherwise when re-mounting we
// will not re-register those event listeners.
SWRGlobalState.delete(provider)
}
}
}
}
initProvider()
// This is a new provider, we need to initialize it and setup DOM events
// listeners for `focus` and `reconnect` actions.
// We might want to inject an extra layer on top of `provider` in the future,
// such as key serialization, auto GC, etc.
// For now, it's just a `Map` interface without any modifications.
return [provider, mutate, initProvider, unmount]
}
return [provider, (SWRGlobalState.get(provider) as GlobalState)[3]]
}