Skip to content
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

Expose Cache #231

Merged
merged 20 commits into from
Feb 24, 2020
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Replace custom cache with exposed cache
sergiodxa committed Jan 19, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 3c4e4910b9254a53a9e66b13dedda567ede1d26e
20 changes: 12 additions & 8 deletions src/cache.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CacheInterface } from './types'
import { mutate } from './use-swr'

export default class Cache implements CacheInterface {
__cache: Map<string, any>
@@ -11,19 +12,22 @@ export default class Cache implements CacheInterface {
return this.__cache.get(key)
}

set(key: string, value: any): any {
return this.__cache.set(key, value)
set(key: string, value: any, shouldNotify = true): any {
this.__cache.set(key, value)
if (shouldNotify) mutate(key, value, false)
}

clear() {
this.__cache.clear()
has(key: string) {
return this.__cache.has(key)
}

delete(key: string) {
this.__cache.delete(key)
clear(shouldNotify = true) {
if (shouldNotify) this.__cache.forEach(key => mutate(key, null, false))
this.__cache.clear()
}

has(key: string) {
return this.__cache.has(key)
delete(key: string, shouldNotify = true) {
if (shouldNotify) mutate(key, null, false)
this.__cache.delete(key)
}
}
3 changes: 1 addition & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -67,8 +67,7 @@ const defaultConfig: ConfigInterface = {
refreshWhenOffline: false,
shouldRetryOnError: true,
suspense: false,
compare: deepEqual,
cache
compare: deepEqual
}

// Focus revalidate
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './use-swr'
import { default as useSWR } from './use-swr'
export { useSWRPages } from './use-swr-pages'
export { cache } from './config'
export {
ConfigInterface,
revalidateType,
8 changes: 3 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -39,8 +39,6 @@ export interface ConfigInterface<
) => void

compare?: (a: Data | undefined, b: Data | undefined) => boolean

cache?: CacheInterface
}

export interface RevalidateOptionInterface {
@@ -114,9 +112,9 @@ export type actionType<Data, Error> = {
}

export interface CacheInterface {
set(key: string, value: any): any
get(key: string): any
delete(key: string): void
set(key: string, value: any, shouldNotify?: boolean): any
has(key: string): boolean
clear(): void
delete(key: string, shouldNotify?: boolean): void
clear(shouldNotify?: boolean): void
}
22 changes: 11 additions & 11 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
@@ -116,8 +116,8 @@ const mutate: mutateInterface = async (_key, _data, shouldRevalidate) => {
}

if (typeof data !== 'undefined') {
// update cached data
cache.set(key, data)
// update cached data, avoid notifying from the cache
cache.set(key, data, false)
}

// update existing SWR Hooks' state
@@ -181,8 +181,8 @@ function useSWR<Data = any, Error = any>(
fn = config.fetcher
}

const initialData = config.cache.get(key) || config.initialData
const initialError = config.cache.get(keyErr)
const initialData = cache.get(key) || config.initialData
const initialError = cache.get(keyErr)

// if a state is accessed (data, error or isValidating),
// we add the state to dependencies so if the state is
@@ -260,7 +260,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 && !config.cache.get(key)) {
if (config.loadingTimeout && !cache.get(key)) {
setTimeout(() => {
if (loading) config.onLoadingSlow(key, config)
}, config.loadingTimeout)
@@ -294,8 +294,8 @@ function useSWR<Data = any, Error = any>(
return false
}

config.cache.set(key, newData)
config.cache.set(keyErr, undefined)
cache.set(key, newData, false)
cache.set(keyErr, undefined, false)
keyRef.current = key

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

config.cache.set(keyErr, err)
cache.set(keyErr, err, false)
keyRef.current = key

// get a new error
@@ -377,7 +377,7 @@ function useSWR<Data = any, Error = any>(
// and trigger a revalidation

const currentHookData = stateRef.current.data
const latestKeyedData = config.cache.get(key) || config.initialData
const latestKeyedData = cache.get(key) || config.initialData

// update the state if the key changed or cache updated
if (
@@ -548,8 +548,8 @@ function useSWR<Data = any, Error = any>(
// (it should be suspended)

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

if (
typeof latestData === 'undefined' &&
93 changes: 51 additions & 42 deletions test/use-swr.test.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import {
} from '@testing-library/react'
import React, { ReactNode, Suspense, useEffect, useState } from 'react'

import useSWR, { mutate, SWRConfig, trigger, Cache } from '../src'
import useSWR, { mutate, SWRConfig, trigger, cache } from '../src'

class ErrorBoundary extends React.Component<{ fallback: ReactNode }> {
state = { hasError: false }
@@ -280,6 +280,56 @@ describe('useSWR', () => {
`"hello, SWR"`
)
})

it('should react to direct cache updates', async () => {
cache.set('custom-cache-key', 'custom cache message')

function Page() {
const { data } = useSWR('custom-cache-key', () => 'random message', {
suspense: true
})
return <div>{data}</div>
}

// render using custom cache
const { queryByText, findByText } = render(
<React.Suspense fallback={null}>
<Page />
</React.Suspense>
)

// content should come from custom cache
expect(queryByText('custom cache message')).toMatchInlineSnapshot(`
<div>
custom cache message
</div>
`)

// content should come from fetcher due lack of cached data
expect(await findByText('random message')).toMatchInlineSnapshot(`
<div>
random message
</div>
`)

act(() => cache.set('custom-cache-key', 'a different message'))

// content should be updated from new cache value
expect(await findByText('a different message')).toMatchInlineSnapshot(`
<div>
a different message
</div>
`)

act(() => cache.delete('custom-cache-key'))

// content should go back to be the fetched value
expect(await findByText('random message')).toMatchInlineSnapshot(`
<div>
random message
</div>
`)
})
})

describe('useSWR - loading', () => {
@@ -356,47 +406,6 @@ describe('useSWR - loading', () => {
expect(renderCount).toEqual(1)
expect(dataLoaded).toEqual(true)
})

it('should use custom cache', async () => {
const cache = new Cache({
'custom-cache-1': 'custom cache message'
})

function Page() {
const { data } = useSWR('custom-cache-1', () => 'random message', {
suspense: true
})
return <div>{data}</div>
}

// render using custom cache
const { queryByText, findByText, rerender } = render(
<SWRConfig value={{ cache }}>
<Page />
</SWRConfig>
)

// content should come from custom cache
expect(queryByText('custom cache message')).toMatchInlineSnapshot(`
<div>
custom cache message
</div>
`)

// render againt with default cache
rerender(
<React.Suspense fallback={null}>
<Page />
</React.Suspense>
)

// content should come from fetcher due lack of cached data
expect(await findByText('random message')).toMatchInlineSnapshot(`
<div>
random message
</div>
`)
})
})

describe('useSWR - refresh', () => {