|
1 |
| -import { useEffect, useRef } from 'react' |
2 |
| -import { Middleware, SWRHook, State, useSWRConfig } from 'swr' |
3 |
| - |
4 |
| -type SwrCache = [string, State][] |
5 |
| - |
6 | 1 | const STORAGE_KEY = 'copilot-swr'
|
7 | 2 |
|
8 |
| -const cachedKeys = new Set<string>() |
9 |
| - |
10 |
| -// keys that have been validated (even if no validator is provided for it) |
11 |
| -const validatedKeys = new Set<string>() |
12 |
| - |
13 |
| -/** |
14 |
| - * This hook should be used before the corresponding useSWR() call, |
15 |
| - * otherwise SWR will consider the fetched data stale and send a new request. |
16 |
| - * |
17 |
| - * @param key - **the key is not supposed to be dynamic.** |
18 |
| - * @param validate - validates the cached state, if it returns false, the state will be discarded. |
19 |
| - * This function will only run once for each key (because we want it to validate cached data, not fresh data). |
20 |
| - */ |
21 |
| -export function useSWRCache( |
22 |
| - key: string, |
23 |
| - validate?: (state: State<unknown>) => boolean, |
24 |
| -) { |
25 |
| - cachedKeys.add(key) |
26 |
| - |
27 |
| - const { cache, mutate } = useSWRConfig() |
28 |
| - |
29 |
| - const validated = useRef(false) |
30 |
| - |
31 |
| - // Only run once. We cannot use useEffect() here because useSWR() fires request synchronously, |
32 |
| - // if we mutate() the state in useEffect(), SWR will think the fetched data is stale, |
33 |
| - // discard it, and attempt to fire a new request, which will be blocked if dedupingInterval is set. |
34 |
| - if (!validated.current) { |
35 |
| - validated.current = true |
36 |
| - |
37 |
| - if (!validatedKeys.has(key)) { |
38 |
| - validatedKeys.add(key) |
39 |
| - |
40 |
| - if (validate) { |
41 |
| - const state = cache.get(key) |
42 |
| - |
43 |
| - if (state) { |
44 |
| - let isValid: boolean |
45 |
| - |
46 |
| - try { |
47 |
| - isValid = validate(state) |
48 |
| - } catch (e) { |
49 |
| - isValid = false |
50 |
| - console.warn(e) |
51 |
| - } |
52 |
| - |
53 |
| - if (!isValid) { |
54 |
| - console.log('[SWR cache]: invalid cache, discarding:', key) |
55 |
| - |
56 |
| - mutate(key, undefined, { revalidate: false }) |
57 |
| - } |
58 |
| - } |
59 |
| - } |
60 |
| - } |
61 |
| - } |
62 |
| -} |
63 |
| - |
64 |
| -/** |
65 |
| - * The only thing this middleware does is to borrow the <SWRConfig>'s lifecycle |
66 |
| - * and clear validatedKeys when it unmounts (happens during hot reload). |
67 |
| - * It should be used in the topmost <SWRConfig>. |
68 |
| - */ |
69 |
| -export const swrCacheMiddleware: Middleware = |
70 |
| - (useSWRNext: SWRHook) => (key, fetcher, config) => { |
71 |
| - useEffect( |
72 |
| - () => () => { |
73 |
| - validatedKeys.clear() |
74 |
| - |
75 |
| - // however, do not clear cachedKeys!! |
76 |
| - // because this unmount callback also runs before the page unloads, |
77 |
| - // and if there's no keys, no cache will be persisted. |
78 |
| - }, |
79 |
| - [], |
80 |
| - ) |
81 |
| - |
82 |
| - return useSWRNext(key, fetcher, config) |
83 |
| - } |
84 |
| - |
85 |
| -// https://swr.vercel.app/docs/advanced/cache#localstorage-based-persistent-cache |
86 |
| -export function localStorageProvider() { |
87 |
| - const map = new Map<string, any>( |
88 |
| - JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'), |
89 |
| - ) |
90 |
| - |
91 |
| - window.addEventListener('beforeunload', () => { |
92 |
| - const cache = Object.fromEntries( |
93 |
| - JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') as SwrCache, |
94 |
| - ) |
95 |
| - |
96 |
| - // overwrite cache with map entries |
97 |
| - Array.from(map.entries()) |
98 |
| - .filter( |
99 |
| - ([key, state]) => cachedKeys.has(key) && state.data && !state.error, |
100 |
| - ) |
101 |
| - .forEach(([key, state]) => (cache[key] = state)) |
102 |
| - |
103 |
| - // remove entries that are no longer needed |
104 |
| - Object.keys(cache).forEach((key) => { |
105 |
| - if (!cachedKeys.has(key)) { |
106 |
| - delete cache[key] |
107 |
| - } |
108 |
| - }) |
109 |
| - |
110 |
| - localStorage.setItem(STORAGE_KEY, JSON.stringify(Object.entries(cache))) |
111 |
| - }) |
112 |
| - |
113 |
| - return map |
| 3 | +// 清理旧版本留下的缓存数据 |
| 4 | +// TODO: 等大部分用户都升级到新版本,就删除这个函数,大概在三个月之后? |
| 5 | +export function clearOutdatedSwrCache() { |
| 6 | + localStorage.removeItem(STORAGE_KEY) |
114 | 7 | }
|
0 commit comments