Skip to content

Micro optimizations #1018

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

Merged
merged 8 commits into from
Mar 13, 2021
Merged
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
30 changes: 15 additions & 15 deletions src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,42 @@ import { Cache as CacheType, Key, CacheListener } from './types'
import hash from './libs/hash'

export default class Cache implements CacheType {
private __cache: Map<string, any>
private __listeners: CacheListener[]
private cache: Map<string, any>
private subs: CacheListener[]

constructor(initialData: any = {}) {
this.__cache = new Map(Object.entries(initialData))
this.__listeners = []
this.cache = new Map(Object.entries(initialData))
this.subs = []
}

get(key: Key): any {
const [_key] = this.serializeKey(key)
return this.__cache.get(_key)
return this.cache.get(_key)
}

set(key: Key, value: any): any {
const [_key] = this.serializeKey(key)
this.__cache.set(_key, value)
this.cache.set(_key, value)
this.notify()
}

keys() {
return Array.from(this.__cache.keys())
return Array.from(this.cache.keys())
}

has(key: Key) {
const [_key] = this.serializeKey(key)
return this.__cache.has(_key)
return this.cache.has(_key)
}

clear() {
this.__cache.clear()
this.cache.clear()
this.notify()
}

delete(key: Key) {
const [_key] = this.serializeKey(key)
this.__cache.delete(_key)
this.cache.delete(_key)
this.notify()
}

Expand Down Expand Up @@ -74,22 +74,22 @@ export default class Cache implements CacheType {
}

let isSubscribed = true
this.__listeners.push(listener)
this.subs.push(listener)

return () => {
if (!isSubscribed) return
isSubscribed = false
const index = this.__listeners.indexOf(listener)
const index = this.subs.indexOf(listener)
if (index > -1) {
this.__listeners[index] = this.__listeners[this.__listeners.length - 1]
this.__listeners.length--
this.subs[index] = this.subs[this.subs.length - 1]
this.subs.length--
}
}
}

// Notify Cache subscribers about a change in the cache
private notify() {
for (let listener of this.__listeners) {
for (let listener of this.subs) {
listener()
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/use-swr-infinite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,20 @@ function useSWRInfinite<Data = any, Error = any>(
// not ready
}

const [, rerender] = useState<boolean>(false)
const rerender = useState({})[1]

// we use cache to pass extra info (context) to fetcher so it can be globally shared
// here we get the key of the fetcher context cache
let contextCacheKey: string | null = null
if (firstPageKey) {
contextCacheKey = 'context@' + firstPageKey
contextCacheKey = 'ctx@' + firstPageKey
}

// page count is cached as well, so when navigating the list can be restored
let pageCountCacheKey: string | null = null
let cachedPageSize
if (firstPageKey) {
pageCountCacheKey = 'size@' + firstPageKey
pageCountCacheKey = 'len@' + firstPageKey
cachedPageSize = cache.get(pageCountCacheKey)
}
const pageCountRef = useRef<number>(cachedPageSize || initialSize)
Expand All @@ -105,10 +105,10 @@ function useSWRInfinite<Data = any, Error = any>(

// actual swr of all pages
const swr = useSWR<Data[], Error>(
firstPageKey ? ['many', firstPageKey] : null,
firstPageKey ? ['inf', firstPageKey] : null,
async () => {
// get the revalidate context
const { originalData, force } = cache.get(contextCacheKey) || {}
const { data: originalData, force } = cache.get(contextCacheKey) || {}

// return an array of page data
const data: Data[] = []
Expand Down Expand Up @@ -174,7 +174,7 @@ function useSWRInfinite<Data = any, Error = any>(
if (shouldRevalidate && typeof data !== 'undefined') {
// we only revalidate the pages that are changed
const originalData = dataRef.current
cache.set(contextCacheKey, { originalData, force: false })
cache.set(contextCacheKey, { data: originalData, force: false })
} else if (shouldRevalidate) {
// calling `mutate()`, we revalidate all pages
cache.set(contextCacheKey, { force: true })
Expand All @@ -197,7 +197,7 @@ function useSWRInfinite<Data = any, Error = any>(
pageCountRef.current = arg
}
cache.set(pageCountCacheKey, pageCountRef.current)
rerender(v => !v)
rerender({})
return mutate(v => v)
},
[mutate, pageCountCacheKey]
Expand Down
40 changes: 17 additions & 23 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ function useSWR<Data = any, Error = any>(
// display the data label in the React DevTools next to SWR hooks
useDebugValue(stateRef.current.data)

const rerender = useState<unknown>(null)[1]
const rerender = useState<unknown>({})[1]

let dispatch = useCallback(
(payload: Action<Data, Error>) => {
Expand Down Expand Up @@ -352,26 +352,21 @@ function useSWR<Data = any, Error = any>(
revalidators: Record<string, Revalidator[]>,
callback: Revalidator
) => {
if (!callback) return
if (!revalidators[key]) {
revalidators[key] = [callback]
} else {
revalidators[key].push(callback)
}
}

const removeRevalidator = (
revlidators: Record<string, Revalidator[]>,
callback: Revalidator
) => {
if (revlidators[key]) {
const revalidators = revlidators[key]
const index = revalidators.indexOf(callback)
return () => {
const keyedRevalidators = revalidators[key]
const index = keyedRevalidators.indexOf(callback)

if (index >= 0) {
// 10x faster than splice
// https://jsperf.com/array-remove-by-index
revalidators[index] = revalidators[revalidators.length - 1]
revalidators.pop()
// O(1): faster than splice
keyedRevalidators[index] =
keyedRevalidators[keyedRevalidators.length - 1]
keyedRevalidators.pop()
}
}
}
Expand Down Expand Up @@ -573,9 +568,8 @@ function useSWR<Data = any, Error = any>(
const latestKeyedData = resolveData()

// update the state if the key changed (not the inital render) or cache updated
if (keyRef.current !== key) {
keyRef.current = key
}
keyRef.current = key

if (!config.compare(currentHookData, latestKeyedData)) {
dispatch({ data: latestKeyedData })
}
Expand Down Expand Up @@ -665,9 +659,9 @@ function useSWR<Data = any, Error = any>(
return false
}

addRevalidator(FOCUS_REVALIDATORS, onFocus)
addRevalidator(RECONNECT_REVALIDATORS, onReconnect)
addRevalidator(CACHE_REVALIDATORS, onUpdate)
const unsubFocus = addRevalidator(FOCUS_REVALIDATORS, onFocus)
const unsubReconnect = addRevalidator(RECONNECT_REVALIDATORS, onReconnect)
const unsubUpdate = addRevalidator(CACHE_REVALIDATORS, onUpdate)

return () => {
// cleanup
Expand All @@ -676,9 +670,9 @@ function useSWR<Data = any, Error = any>(
// mark it as unmounted
unmountedRef.current = true

removeRevalidator(FOCUS_REVALIDATORS, onFocus)
removeRevalidator(RECONNECT_REVALIDATORS, onReconnect)
removeRevalidator(CACHE_REVALIDATORS, onUpdate)
unsubFocus()
unsubReconnect()
unsubUpdate()
}
}, [key, revalidate])

Expand Down
2 changes: 1 addition & 1 deletion test/use-swr-configs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { sleep } from './utils'
describe('useSWR - configs', () => {
it('should read the config fallback from the context', async () => {
let value = 0
const INTERVAL = 10
const INTERVAL = 50
const fetcher = () => value++

function Section() {
Expand Down