Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@
"husky": "2.4.1",
"jest": "24.9.0",
"lint-staged": "8.2.1",
"prettier": "1.18.2",
"prettier": "1.19",
"react": "16.11.0",
"react-dom": "16.11.0",
"ts-jest": "24.1.0",
"typescript": "3.6.4"
"typescript": "3.7"
},
"dependencies": {
"fast-deep-equal": "2.0.1"
Expand Down
19 changes: 15 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,24 @@ import {
} from './types'

// Cache
const __cache = new Map()
type CacheItem = {
key: string | any[]
data: any
err: any
}

const __cache = new Map<string, CacheItem>()

function cache() {
return __cache
}

function cacheGet(key: string): any {
function cacheGet(key: string): undefined | CacheItem {
return __cache.get(key)
}

function cacheSet(key: string, value: any) {
return __cache.set(key, value)
function cacheSet(key: string, item: CacheItem) {
__cache.set(key, item)
}

function cacheClear() {
Expand Down Expand Up @@ -103,6 +113,7 @@ export {
FOCUS_REVALIDATORS,
CACHE_REVALIDATORS,
MUTATION_TS,
cache,
cacheGet,
cacheSet,
cacheClear
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ export type triggerInterface = (
key: keyInterface,
shouldRevalidate?: boolean
) => void
export type mutateManyInterface = (
selectiveMutator: (
key: string | any[]
) => void | {
update(data: any): any
shouldRevalidate?: boolean
}
) => void
export type mutateInterface<Data = any> = (
key: keyInterface,
data: Data | Promise<Data>,
Expand Down
12 changes: 8 additions & 4 deletions src/use-swr-pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ export function useSWRPages<OffsetType = any, Data = any, Error = any>(
const pageOffsetKey = `_swr_page_offset_` + pageKey

const [pageCount, setPageCount] = useState<number>(
cacheGet(pageCountKey) || 1
cacheGet(pageCountKey)?.data || 1
)
const [pageOffsets, setPageOffsets] = useState<OffsetType[]>(
cacheGet(pageOffsetKey) || [null]
cacheGet(pageOffsetKey)?.data || [null]
)
const [pageSWRs, setPageSWRs] = useState<responseInterface<Data, Error>[]>([])

Expand Down Expand Up @@ -134,7 +134,7 @@ export function useSWRPages<OffsetType = any, Data = any, Error = any>(
const loadMore = useCallback(() => {
if (isLoadingMore || isReachingEnd) return
setPageCount(c => {
cacheSet(pageCountKey, c + 1)
cacheSet(pageCountKey, { key: pageCountKey, data: c + 1, err: undefined })
return c + 1
})
}, [isLoadingMore || isReachingEnd])
Expand Down Expand Up @@ -166,7 +166,11 @@ export function useSWRPages<OffsetType = any, Data = any, Error = any>(
setPageOffsets(arr => {
const _arr = [...arr]
_arr[id + 1] = newPageOffset
cacheSet(pageOffsetKey, _arr)
cacheSet(pageOffsetKey, {
key: pageOffsetKey,
data: _arr,
err: undefined
})
return _arr
})
}
Expand Down
77 changes: 46 additions & 31 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'react'

import defaultConfig, {
cache,
cacheGet,
cacheSet,
CACHE_REVALIDATORS,
Expand All @@ -29,6 +30,7 @@ import {
fetcherFn,
keyInterface,
mutateInterface,
mutateManyInterface,
responseInterface,
RevalidateOptionInterface,
triggerInterface,
Expand All @@ -43,7 +45,6 @@ const IS_SERVER = typeof window === 'undefined'
const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect

// TODO: introduce namepsace for the cache
const getErrorKey = key => (key ? 'err@' + key : '')
const getKeyArgs = key => {
let args = null
if (typeof key === 'function') {
Expand Down Expand Up @@ -73,10 +74,9 @@ const trigger: triggerInterface = (_key, shouldRevalidate = true) => {

const updaters = CACHE_REVALIDATORS[key]
if (key && updaters) {
const currentData = cacheGet(key)
const currentError = cacheGet(getErrorKey(key))
const current = cacheGet(key) // TODO maybe add ?? {}
for (let i = 0; i < updaters.length; ++i) {
updaters[i](shouldRevalidate, currentData, currentError, true)
updaters[i](shouldRevalidate, current.data, current.err, true)
}
}
}
Expand All @@ -90,8 +90,18 @@ const broadcastState: broadcastStateInterface = (key, data, error) => {
}
}

const mutateMany: mutateManyInterface = async selectiveMutator => {
for (const [internalKey, { key, data }] of cache()) {
const r = selectiveMutator(key)
if (r) {
const { update, shouldRevalidate } = r
mutate(internalKey, update(data), shouldRevalidate)
}
}
}

const mutate: mutateInterface = async (_key, _data, shouldRevalidate) => {
const [key] = getKeyArgs(_key)
const [key, fnArgs] = getKeyArgs(_key)
if (!key) return

// update timestamp
Expand All @@ -118,7 +128,12 @@ const mutate: mutateInterface = async (_key, _data, shouldRevalidate) => {

if (typeof data !== 'undefined') {
// update cached data
cacheSet(key, data)
const item = cacheGet(key) ?? {
key: fnArgs || key,
data: undefined,
err: undefined
}
cacheSet(key, { ...item, data })
}

// update existing SWR Hooks' state
Expand Down Expand Up @@ -167,9 +182,6 @@ function useSWR<Data = any, Error = any>(
// (because `revalidate` only depends on `key`)
const [key, fnArgs] = getKeyArgs(_key)

// `keyErr` is the cache key for error objects
const keyErr = getErrorKey(key)

config = Object.assign(
{},
defaultConfig,
Expand All @@ -182,8 +194,12 @@ function useSWR<Data = any, Error = any>(
fn = config.fetcher
}

const initialData = cacheGet(key) || config.initialData
const initialError = cacheGet(keyErr)
const initial = cacheGet(key) ?? {
key: fnArgs || key,
data: config.initialData,
err: undefined
}
cacheSet(key, initial)

// if a state is accessed (data, error or isValidating),
// we add the state to dependencies so if the state is
Expand All @@ -194,8 +210,8 @@ function useSWR<Data = any, Error = any>(
isValidating: false
})
const stateRef = useRef({
data: initialData,
error: initialError,
data: initial.data,
error: initial.err,
isValidating: false
})

Expand Down Expand Up @@ -261,7 +277,7 @@ function useSWR<Data = any, Error = any>(

// if no cache being rendered currently (it shows a blank page),
// we trigger the loading slow event.
if (config.loadingTimeout && !cacheGet(key)) {
if (config.loadingTimeout && !cacheGet(key)?.data) {
setTimeout(() => {
if (loading) config.onLoadingSlow(key, config)
}, config.loadingTimeout)
Expand Down Expand Up @@ -295,8 +311,7 @@ function useSWR<Data = any, Error = any>(
return false
}

cacheSet(key, newData)
cacheSet(keyErr, undefined)
cacheSet(key, { key: fnArgs || key, data: newData, err: undefined })
keyRef.current = key

// new state for the reducer
Expand Down Expand Up @@ -327,7 +342,7 @@ function useSWR<Data = any, Error = any>(
delete CONCURRENT_PROMISES[key]
delete CONCURRENT_PROMISES_TS[key]

cacheSet(keyErr, err)
cacheGet(key).err = err
keyRef.current = key

// get a new error
Expand Down Expand Up @@ -378,7 +393,7 @@ function useSWR<Data = any, Error = any>(
// and trigger a revalidation

const currentHookData = stateRef.current.data
const latestKeyedData = cacheGet(key) || config.initialData
const latestKeyedData = cacheGet(key)?.data || config.initialData

// update the state if the key changed or cache updated
if (
Expand Down Expand Up @@ -516,7 +531,8 @@ function useSWR<Data = any, Error = any>(
if (
!stateRef.current.error &&
(config.refreshWhenHidden || isDocumentVisible()) &&
(!config.refreshWhenOffline && isOnline())
!config.refreshWhenOffline &&
isOnline()
) {
// only revalidate when the page is visible
// if API request errored, we stop polling in this round
Expand Down Expand Up @@ -549,12 +565,11 @@ function useSWR<Data = any, Error = any>(
// (it should be suspended)

// try to get data and error from cache
let latestData = cacheGet(key)
let latestError = cacheGet(keyErr)
let latest = cacheGet(key)

if (
typeof latestData === 'undefined' &&
typeof latestError === 'undefined'
typeof latest.data === 'undefined' &&
typeof latest.err === 'undefined'
) {
// need to start the request if it hasn't
if (!CONCURRENT_PROMISES[key]) {
Expand All @@ -572,19 +587,19 @@ function useSWR<Data = any, Error = any>(
}

// it's a value, return it directly (override)
latestData = CONCURRENT_PROMISES[key]
latest.data = CONCURRENT_PROMISES[key]
}

if (typeof latestData === 'undefined' && latestError) {
if (typeof latest.data === 'undefined' && latest.err) {
// in suspense mode, throw error if there's no content
throw latestError
throw latest.err
}

// return the latest data / error from cache
// in case `key` has changed
return {
error: latestError,
data: latestData,
error: latest.err,
data: latest.data,
revalidate,
isValidating: stateRef.current.isValidating
}
Expand All @@ -601,13 +616,13 @@ function useSWR<Data = any, Error = any>(
// so we need to match the latest key and data (fallback to `initialData`)
get: function() {
stateDependencies.current.error = true
return keyRef.current === key ? stateRef.current.error : initialError
return keyRef.current === key ? stateRef.current.error : initial.err
}
},
data: {
get: function() {
stateDependencies.current.data = true
return keyRef.current === key ? stateRef.current.data : initialData
return keyRef.current === key ? stateRef.current.data : initial.data
}
},
isValidating: {
Expand All @@ -624,5 +639,5 @@ function useSWR<Data = any, Error = any>(

const SWRConfig = SWRConfigContext.Provider

export { trigger, mutate, SWRConfig }
export { trigger, mutateMany, mutate, SWRConfig }
export default useSWR
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"downlevelIteration": true,
"outDir": "./dist",
"types": [
"node", "jest"
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3594,10 +3594,10 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=

prettier@1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
prettier@1.19:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==

pretty-format@^24.3.0, pretty-format@^24.9.0:
version "24.9.0"
Expand Down Expand Up @@ -4543,10 +4543,10 @@ type-fest@^0.6.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==

typescript@3.6.4:
version "3.6.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d"
integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==
typescript@3.7:
version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==

uglify-js@^3.1.4:
version "3.6.9"
Expand Down