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

add endpoint, type and forced to BaseQueryApi and prepareHeaders #1656

Merged
merged 5 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
11 changes: 11 additions & 0 deletions packages/toolkit/src/query/baseQueryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ export interface BaseQueryApi {
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
extra: unknown
endpoint: string
type: 'query' | 'mutation'
/**
* Only available for queries: indicates if a query has been forced,
* i.e. it would have been fetched even if there would already be a cache entry
* (this does not mean that there is already a cache entry though!)
*
* This can be used to for example add a `Cache-Control: no-cache` header for
* invalidated queries.
*/
forced?: boolean
}

export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
Expand Down
2 changes: 2 additions & 0 deletions packages/toolkit/src/query/core/buildInitiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
endpointName,
})
const thunk = queryThunk({
type: 'query',
subscribe,
forceRefetch,
subscriptionOptions,
Expand Down Expand Up @@ -292,6 +293,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
return (arg, { track = true, fixedCacheKey } = {}) =>
(dispatch, getState) => {
const thunk = mutationThunk({
type: 'mutation',
endpointName,
originalArgs: arg,
track,
Expand Down
1 change: 1 addition & 0 deletions packages/toolkit/src/query/core/buildMiddleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function buildMiddleware<
override: Partial<QueryThunkArg> = {}
) {
return queryThunk({
type: 'query',
endpointName: querySubState.endpointName,
originalArgs: querySubState.originalArgs,
subscribe: false,
Expand Down
49 changes: 34 additions & 15 deletions packages/toolkit/src/query/core/buildThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,13 @@ export interface Matchers<
export interface QueryThunkArg
extends QuerySubstateIdentifier,
StartQueryActionCreatorOptions {
type: 'query'
originalArgs: unknown
endpointName: string
}

export interface MutationThunkArg {
type: 'mutation'
originalArgs: unknown
endpointName: string
track?: boolean
Expand Down Expand Up @@ -276,6 +278,10 @@ export function buildThunks<
dispatch,
getState,
extra,
endpoint: arg.endpointName,
type: arg.type,
forced:
arg.type === 'query' ? isForcedQuery(arg, getState()) : undefined,
}
if (endpointDefinition.query) {
result = await baseQuery(
Expand Down Expand Up @@ -325,6 +331,28 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
}
}

function isForcedQuery(
arg: QueryThunkArg,
state: RootState<any, string, ReducerPath>
) {
const requestState = state[reducerPath]?.queries?.[arg.queryCacheKey]
const baseFetchOnMountOrArgChange =
state[reducerPath]?.config.refetchOnMountOrArgChange

const fulfilledVal = requestState?.fulfilledTimeStamp
const refetchVal =
arg.forceRefetch ?? (arg.subscribe && baseFetchOnMountOrArgChange)

if (refetchVal) {
// Return if its true or compare the dates because it must be a number
return (
refetchVal === true ||
(Number(new Date()) - Number(fulfilledVal)) / 1000 >= refetchVal
)
}
return false
}

const queryThunk = createAsyncThunk<
ThunkResult,
QueryThunkArg,
Expand All @@ -334,29 +362,20 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
return { startedTimeStamp: Date.now() }
},
condition(arg, { getState }) {
const state = getState()[reducerPath]
const requestState = state?.queries?.[arg.queryCacheKey]
const baseFetchOnMountOrArgChange = state.config.refetchOnMountOrArgChange

const state = getState()
const requestState = state[reducerPath]?.queries?.[arg.queryCacheKey]
const fulfilledVal = requestState?.fulfilledTimeStamp
const refetchVal =
arg.forceRefetch ?? (arg.subscribe && baseFetchOnMountOrArgChange)

// Don't retry a request that's currently in-flight
if (requestState?.status === 'pending') return false

// if this is forced, continue
if (isForcedQuery(arg, state)) return true

// Pull from the cache unless we explicitly force refetch or qualify based on time
if (fulfilledVal) {
if (refetchVal) {
// Return if its true or compare the dates because it must be a number
return (
refetchVal === true ||
(Number(new Date()) - Number(fulfilledVal)) / 1000 >= refetchVal
)
}
if (fulfilledVal)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved that out to also be able to call it elsewhere. Could someone (@msutkowski you were working on this with me?) please verify that I didn't break the logic?

// Value is cached and we didn't specify to refresh, skip it.
return false
}

return true
},
Expand Down
8 changes: 4 additions & 4 deletions packages/toolkit/src/query/fetchBaseQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { joinUrls } from './utils'
import { isPlainObject } from '@reduxjs/toolkit'
import type { BaseQueryFn } from './baseQueryTypes'
import type { BaseQueryApi, BaseQueryFn } from './baseQueryTypes'
import type { MaybePromise, Override } from './tsHelpers'

export type ResponseHandler =
Expand Down Expand Up @@ -113,7 +113,7 @@ export type FetchBaseQueryArgs = {
baseUrl?: string
prepareHeaders?: (
headers: Headers,
api: { getState: () => unknown }
api: Pick<BaseQueryApi, 'getState' | 'endpoint' | 'type' | 'forced'>
msutkowski marked this conversation as resolved.
Show resolved Hide resolved
) => MaybePromise<Headers>
fetchFn?: (
input: RequestInfo,
Expand Down Expand Up @@ -178,7 +178,7 @@ export function fetchBaseQuery({
'Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.'
)
}
return async (arg, { signal, getState }) => {
return async (arg, { signal, getState, endpoint, forced, type }) => {
let meta: FetchBaseQueryMeta | undefined
let {
url,
Expand All @@ -200,7 +200,7 @@ export function fetchBaseQuery({

config.headers = await prepareHeaders(
new Headers(stripUndefined(headers)),
{ getState }
{ getState, endpoint, forced, type }
)

// Only set the content-type to json if appropriate. Will not be true for FormData, ArrayBuffer, Blob, etc.
Expand Down
Loading