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

Refine createCache #1283

Merged
merged 12 commits into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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 src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export interface Configuration<

isOnline: () => boolean
isDocumentVisible: () => boolean
registerOnFocus: (cb: () => void) => void
registerOnReconnect: (cb: () => void) => void
setupOnFocus: (cb: () => void) => () => void
setupOnReconnect: (cb: () => void) => () => void

/**
* @deprecated `revalidateOnMount` will be removed. Please considering using the `revalidateWhenStale` option.
Expand Down
25 changes: 19 additions & 6 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,31 @@ const getGlobalState = (cache: Cache) => {
]
}

// Setup DOM events listeners for `focus` and `reconnect` actions
if (!IS_SERVER) {
function setupGlobalEvents(config: Configuration) {
const [FOCUS_REVALIDATORS, RECONNECT_REVALIDATORS] = getGlobalState(
defaultConfig.cache
config.cache
)
const revalidate = (revalidators: Record<string, Revalidator[]>) => {
if (!defaultConfig.isDocumentVisible() || !defaultConfig.isOnline()) return
if (!config.isDocumentVisible() || !config.isOnline()) return
huozhi marked this conversation as resolved.
Show resolved Hide resolved

for (const key in revalidators) {
if (revalidators[key][0]) revalidators[key][0]()
}
}
defaultConfig.registerOnFocus(() => revalidate(FOCUS_REVALIDATORS))
defaultConfig.registerOnReconnect(() => revalidate(RECONNECT_REVALIDATORS))
const onFocus = () => revalidate(FOCUS_REVALIDATORS)
const onReconnect = () => revalidate(RECONNECT_REVALIDATORS)
const teardownOnFoucs = config.setupOnFocus(onFocus)
huozhi marked this conversation as resolved.
Show resolved Hide resolved
const teardownOnReconnect = config.setupOnReconnect(onReconnect)

return () => {
teardownOnFoucs()
teardownOnReconnect()
}
}

// Setup DOM events listeners for `focus` and `reconnect` actions
if (!IS_SERVER) setupGlobalEvents(defaultConfig)

const broadcastState: Broadcaster = (
cache: Cache,
key,
Expand Down Expand Up @@ -488,6 +497,10 @@ export function useSWRHandler<Data = any, Error = any>(
configRef.current = config
})

useIsomorphicLayoutEffect(() => {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
return setupGlobalEvents(configRef.current)
}, [configRef.current.cache])

// After mounted or key changed.
useIsomorphicLayoutEffect(() => {
if (!key) return UNDEFINED
Expand Down
73 changes: 42 additions & 31 deletions src/utils/web-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,63 @@ import { isUndefined } from './helper'
*/
let online = true
const isOnline = () => online
const createNoop = (ret?: any) => () => ret
const add = 'addEventListener'
const remove = 'removeEventListener'

// For node and React Native, `window.addEventListener` doesn't exist.
const addWindowEventListener =
typeof window !== 'undefined' && !isUndefined(window.addEventListener)
? window.addEventListener.bind(window)
: null
const addDocumentEventListener =
typeof document !== 'undefined'
? document.addEventListener.bind(document)
: null
type EventAction = 'addEventListener' | 'removeEventListener'

const hasWindow = typeof window !== 'undefined'
const hasDocument = typeof document !== 'undefined'

// For node and React Native, `add/removeEventListener` doesn't exist on window.
const windowEventListener = (action: EventAction) =>
hasWindow && !isUndefined(window[action])
? window[action].bind(window)
: createNoop()
const documentEventListener = (action: EventAction) =>
hasDocument ? document[action].bind(document) : createNoop()

const isDocumentVisible = () => {
if (addDocumentEventListener) {
const visibilityState = document.visibilityState
if (!isUndefined(visibilityState)) {
return visibilityState !== 'hidden'
}
const visibilityState = hasDocument && document.visibilityState
if (!isUndefined(visibilityState)) {
return visibilityState !== 'hidden'
}
// always assume it's visible
return true
}

const registerOnFocus = (cb: () => void) => {
if (addWindowEventListener && addDocumentEventListener) {
// focus revalidate
addDocumentEventListener('visibilitychange', cb)
addWindowEventListener('focus', cb)
const setupOnFocus = (cb: () => void) => {
// focus revalidate
documentEventListener(add)('visibilitychange', cb)
windowEventListener(add)('focus', cb)
return () => {
documentEventListener(remove)('visibilitychange', cb)
windowEventListener(remove)('focus', cb)
}
huozhi marked this conversation as resolved.
Show resolved Hide resolved
}

const registerOnReconnect = (cb: () => void) => {
if (addWindowEventListener) {
// reconnect revalidate
addWindowEventListener('online', () => {
online = true
cb()
})
const setupOnReconnect = (cb: () => void) => {
const onOnline = () => {
online = true
cb()
}
const onOffline = () => {
online = false
}
// reconnect revalidate
windowEventListener(add)('online', onOnline)
// nothing to revalidate, just update the status
windowEventListener(add)('offline', onOffline)

// nothing to revalidate, just update the status
addWindowEventListener('offline', () => (online = false))
return () => {
windowEventListener(remove)('online', onOnline)
windowEventListener(remove)('offline', onOffline)
}
huozhi marked this conversation as resolved.
Show resolved Hide resolved
}

export default {
isOnline,
isDocumentVisible,
registerOnFocus,
registerOnReconnect
setupOnFocus,
setupOnReconnect
} as const
Empty file removed test/index.js
Empty file.
24 changes: 23 additions & 1 deletion test/use-swr-focus.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { act, fireEvent, render, screen } from '@testing-library/react'
import React, { useState } from 'react'
import useSWR from 'swr'
import useSWR, { createCache } from 'swr'
import { sleep } from './utils'

const waitForNextTick = () => act(() => sleep(1))
Expand Down Expand Up @@ -200,4 +200,26 @@ describe('useSWR - focus', () => {
await focusWindow()
await screen.findByText('data: 5')
})

it('should revalidate on focus even with custom cache', async () => {
let value = 0
const { cache } = createCache(new Map())

function Page() {
const { data } = useSWR('revalidateOnFocus + cache', () => value++, {
cache,
revalidateOnFocus: true,
dedupingInterval: 0
})
return <div>data: {data}</div>
}

// reuse default test case
render(<Page />)
screen.getByText('data:')
await screen.findByText('data: 0')
await waitForNextTick()
await focusWindow()
await screen.findByText('data: 1')
})
})