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

feat: Keep track updatedAt as part of the SWR state #1708

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
8 changes: 6 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,15 @@ export type Broadcaster<Data = any, Error = any> = (
error?: Error,
isValidating?: boolean,
revalidate?: boolean,
populateCache?: boolean
populateCache?: boolean,
updatedAt?: number
) => Promise<Data>

export type State<Data, Error> = {
data?: Data
error?: Error
isValidating?: boolean
updatedAt?: number
}

export type Mutator<Data = any> = (
Expand Down Expand Up @@ -204,6 +206,7 @@ export interface SWRResponse<Data = any, Error = any> {
data?: Data
error?: Error
mutate: KeyedMutator<Data>
updatedAt?: number
isValidating: boolean
}

Expand Down Expand Up @@ -237,7 +240,8 @@ export type RevalidateCallback = <K extends RevalidateEvent>(
export type StateUpdateCallback<Data = any, Error = any> = (
data?: Data,
error?: Error,
isValidating?: boolean
isValidating?: boolean,
updatedAt?: number
) => void

export interface Cache<Data = any> {
Expand Down
38 changes: 33 additions & 5 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,16 @@ export const useSWRHandler = <Data = any, Error = any>(
}
const isValidating = resolveValidating()

const updatedAtRef = useRef<number | undefined>()
const getUpdatedAt = () => updatedAtRef.current
const updatedAt = getUpdatedAt()

const [stateRef, stateDependencies, setState] = useStateWithDeps<Data, Error>(
{
data,
error,
isValidating
isValidating,
updatedAt
},
unmountedRef
)
Expand Down Expand Up @@ -210,6 +215,9 @@ export const useSWRHandler = <Data = any, Error = any>(
newData = await newData

if (shouldStartNewRequest) {
const now = Date.now()
updatedAtRef.current = now
newState.updatedAt = now
// If the request isn't interrupted, clean it up after the
// deduplication interval.
setTimeout(cleanupState, config.dedupingInterval)
Expand Down Expand Up @@ -328,7 +336,16 @@ export const useSWRHandler = <Data = any, Error = any>(
// Here is the source of the request, need to tell all other hooks to
// update their states.
if (isCurrentKeyMounted() && shouldStartNewRequest) {
broadcastState(cache, key, newState.data, newState.error, false)
broadcastState(
cache,
key,
newState.data,
newState.error,
false,
false,
undefined,
newState.updatedAt
)
}

return true
Expand Down Expand Up @@ -376,13 +393,19 @@ export const useSWRHandler = <Data = any, Error = any>(
const onStateUpdate: StateUpdateCallback<Data, Error> = (
updatedData,
updatedError,
updatedIsValidating
updatedIsValidating,
nextUpdatedAt
) => {
updatedAtRef.current = isUndefined(nextUpdatedAt)
? getUpdatedAt()
: nextUpdatedAt

setState(
mergeObjects(
{
error: updatedError,
isValidating: updatedIsValidating
isValidating: updatedIsValidating,
updatedAt: getUpdatedAt()
},
// Since `setState` only shallowly compares states, we do a deep
// comparison here.
Expand Down Expand Up @@ -433,7 +456,8 @@ export const useSWRHandler = <Data = any, Error = any>(
setState({
data,
error,
isValidating
isValidating,
updatedAt: UNDEFINED
})
}

Expand Down Expand Up @@ -526,6 +550,10 @@ export const useSWRHandler = <Data = any, Error = any>(
get isValidating() {
stateDependencies.isValidating = true
return isValidating
},
get updatedAt() {
stateDependencies.updatedAt = true
return getUpdatedAt()
}
} as SWRResponse<Data, Error>
}
Expand Down
5 changes: 3 additions & 2 deletions src/utils/broadcast-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const broadcastState: Broadcaster = (
error,
isValidating,
revalidate,
broadcast = true
broadcast = true,
updatedAt
) => {
const [EVENT_REVALIDATORS, STATE_UPDATERS, , FETCH] = SWRGlobalState.get(
cache
Expand All @@ -20,7 +21,7 @@ export const broadcastState: Broadcaster = (
// Cache was populated, update states of all hooks.
if (broadcast && updaters) {
for (let i = 0; i < updaters.length; ++i) {
updaters[i](data, error, isValidating)
updaters[i](data, error, isValidating, updatedAt)
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/utils/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export const useStateWithDeps = <Data, Error, S = State<Data, Error>>(
const stateDependenciesRef = useRef<StateDeps>({
data: false,
error: false,
isValidating: false
isValidating: false,
updatedAt: false
})

/**
Expand Down
2 changes: 1 addition & 1 deletion test/use-swr-loading.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe('useSWR - loading', () => {
}

renderWithConfig(<Page />)
screen.getByText('data,error,isValidating,mutate')
screen.getByText('data,error,isValidating,mutate,updatedAt')
})

it('should sync loading states', async () => {
Expand Down
Loading