Skip to content

Commit

Permalink
Support providing a context
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Galloway committed Oct 18, 2021
1 parent 255f7c6 commit ed74e80
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 30 deletions.
File renamed without changes.
1 change: 1 addition & 0 deletions src/core/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface HydrateOptions {
queries?: QueryOptions
mutations?: MutationOptions
}
context?: React.Context<QueryClient | undefined>
}

interface DehydratedMutation {
Expand Down
11 changes: 7 additions & 4 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,14 @@ export function parseFilterArgs<
: [arg1 || {}, arg2]) as [TFilters, TOptions]
}

export function parseMutationFilterArgs(
export function parseMutationFilterArgs<TOptions = unknown>(
arg1?: QueryKey | MutationFilters,
arg2?: MutationFilters
): MutationFilters | undefined {
return isQueryKey(arg1) ? { ...arg2, mutationKey: arg1 } : arg1
arg2?: MutationFilters | TOptions,
arg3?: TOptions
): [MutationFilters | undefined, TOptions | undefined] {
return (isQueryKey(arg1)
? [{ ...arg2, mutationKey: arg1 }, arg3]
: [arg1, arg2]) as [MutationFilters | undefined, TOptions | undefined]
}

export function mapQueryStatusFilter(
Expand Down
18 changes: 14 additions & 4 deletions src/devtools/devtools.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'

import { Query, useQueryClient } from 'react-query'
import { Query, useQueryClient, QueryClient } from 'react-query'
import { matchSorter } from 'match-sorter'
import useLocalStorage from './useLocalStorage'
import { useIsMounted, useSafeState } from './utils'
Expand Down Expand Up @@ -58,6 +58,10 @@ interface DevtoolsOptions {
* Defaults to 'footer'.
*/
containerElement?: string | any
/**
* Use this to pass your React Query context. Otherwise, the default will be used.
*/
context?: React.Context<QueryClient | undefined>
}

interface DevtoolsPanelOptions {
Expand All @@ -81,6 +85,10 @@ interface DevtoolsPanelOptions {
* Handles the opening and closing the devtools panel
*/
handleDragStart: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
/**
* Use this to pass your React Query context. Otherwise, the default will be used.
*/
context?: React.Context<QueryClient | undefined>
}

const isServer = typeof window === 'undefined'
Expand All @@ -92,6 +100,7 @@ export function ReactQueryDevtools({
toggleButtonProps = {},
position = 'bottom-left',
containerElement: Container = 'footer',
context,
}: DevtoolsOptions): React.ReactElement | null {
const rootRef = React.useRef<HTMLDivElement>(null)
const panelRef = React.useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -224,7 +233,8 @@ export function ReactQueryDevtools({
<Container ref={rootRef} className="ReactQueryDevtools">
<ThemeProvider theme={theme}>
<ReactQueryDevtoolsPanel
ref={panelRef as any}
ref={panelRef}
context={context}
{...otherPanelProps}
style={{
position: 'fixed',
Expand Down Expand Up @@ -365,9 +375,9 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
HTMLDivElement,
DevtoolsPanelOptions
>(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
const { isOpen = true, setIsOpen, handleDragStart, ...panelProps } = props
const { isOpen = true, setIsOpen, handleDragStart, context, ...panelProps } = props

const queryClient = useQueryClient()
const queryClient = useQueryClient({ context })
const queryCache = queryClient.getQueryCache()

const [sort, setSort] = useLocalStorage(
Expand Down
4 changes: 2 additions & 2 deletions src/react/Hydrate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import React from 'react'
import { hydrate, HydrateOptions } from '../core'
import { useQueryClient } from './QueryClientProvider'

export function useHydrate(state: unknown, options?: HydrateOptions) {
const queryClient = useQueryClient()
export function useHydrate(state: unknown, options: HydrateOptions = {}) {
const queryClient = useQueryClient({ context: options.context })

const optionsRef = React.useRef(options)
optionsRef.current = options
Expand Down
23 changes: 18 additions & 5 deletions src/react/QueryClientProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ declare global {
const defaultContext = React.createContext<QueryClient | undefined>(undefined)
const QueryClientSharingContext = React.createContext<boolean>(false)

// if contextSharing is on, we share the first and at least one
// If we are given a context, we will use it.
// Otherwise, if contextSharing is on, we share the first and at least one
// instance of the context across the window
// to ensure that if React Query is used across
// different bundles or microfrontends they will
// all use the same **instance** of context, regardless
// of module scoping.
function getQueryClientContext(contextSharing: boolean) {
function getQueryClientContext(
context: React.Context<QueryClient | undefined> | undefined,
contextSharing: boolean
) {
if (context) {
return context
}
if (contextSharing && typeof window !== 'undefined') {
if (!window.ReactQueryClientContext) {
window.ReactQueryClientContext = defaultContext
Expand All @@ -29,9 +36,13 @@ function getQueryClientContext(contextSharing: boolean) {
return defaultContext
}

export const useQueryClient = () => {
export const useQueryClient = ({
context,
}: {
context?: React.Context<QueryClient | undefined>
} = {}) => {
const queryClient = React.useContext(
getQueryClientContext(React.useContext(QueryClientSharingContext))
getQueryClientContext(context, React.useContext(QueryClientSharingContext))
)

if (!queryClient) {
Expand All @@ -44,12 +55,14 @@ export const useQueryClient = () => {
export interface QueryClientProviderProps {
client: QueryClient
contextSharing?: boolean
context?: React.Context<QueryClient | undefined>
}

export const QueryClientProvider: React.FC<QueryClientProviderProps> = ({
client,
contextSharing = false,
children,
context,
}) => {
React.useEffect(() => {
client.mount()
Expand All @@ -58,7 +71,7 @@ export const QueryClientProvider: React.FC<QueryClientProviderProps> = ({
}
}, [client])

const Context = getQueryClientContext(contextSharing)
const Context = getQueryClientContext(context, contextSharing)

return (
<QueryClientSharingContext.Provider value={contextSharing}>
Expand Down
10 changes: 8 additions & 2 deletions src/react/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MutationFunction,
MutateOptions,
} from '../core/types'
import type { QueryClient } from '../core/queryClient'

export interface UseBaseQueryOptions<
TQueryFnData = unknown,
Expand All @@ -23,7 +24,9 @@ export interface UseBaseQueryOptions<
TData,
TQueryData,
TQueryKey
> {}
> {
context?: React.Context<QueryClient | undefined>
}

export interface UseQueryOptions<
TQueryFnData = unknown,
Expand All @@ -50,7 +53,9 @@ export interface UseInfiniteQueryOptions<
TData,
TQueryData,
TQueryKey
> {}
> {
context?: React.Context<QueryClient | undefined>
}

export type UseBaseQueryResult<
TData = unknown,
Expand Down Expand Up @@ -97,6 +102,7 @@ export interface UseMutationOptions<
retry?: RetryValue<TError>
retryDelay?: RetryDelayValue<TError>
useErrorBoundary?: boolean | ((error: TError) => boolean)
context?: React.Context<QueryClient | undefined>
}

export type UseMutateFunction<
Expand Down
2 changes: 1 addition & 1 deletion src/react/useBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function useBaseQuery<
const mountedRef = React.useRef(false)
const [, forceUpdate] = React.useState(0)

const queryClient = useQueryClient()
const queryClient = useQueryClient({ context: options.context })
const errorResetBoundary = useQueryErrorResetBoundary()
const defaultedOptions = queryClient.defaultQueryObserverOptions(options)

Expand Down
18 changes: 13 additions & 5 deletions src/react/useIsFetching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import React from 'react'

import { notifyManager } from '../core/notifyManager'
import { QueryKey } from '../core/types'
import { QueryClient } from '../core/queryClient'
import { parseFilterArgs, QueryFilters } from '../core/utils'
import { useQueryClient } from './QueryClientProvider'

export function useIsFetching(filters?: QueryFilters): number
interface Options {
context?: React.Context<QueryClient | undefined>
}

export function useIsFetching(filters?: QueryFilters, options?: Options): number
export function useIsFetching(
queryKey?: QueryKey,
filters?: QueryFilters
filters?: QueryFilters,
options?: Options
): number
export function useIsFetching(
arg1?: QueryKey | QueryFilters,
arg2?: QueryFilters
arg2?: QueryFilters | Options,
arg3?: Options
): number {
const mountedRef = React.useRef(false)

const queryClient = useQueryClient()
const [filters, options = {}] = parseFilterArgs(arg1, arg2, arg3)

const queryClient = useQueryClient({ context: options.context })

const [filters] = parseFilterArgs(arg1, arg2)
const [isFetching, setIsFetching] = React.useState(
queryClient.isFetching(filters)
)
Expand Down
20 changes: 15 additions & 5 deletions src/react/useIsMutating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@ import React from 'react'

import { notifyManager } from '../core/notifyManager'
import { QueryKey } from '../core/types'
import { QueryClient } from '../core/queryClient'
import { MutationFilters, parseMutationFilterArgs } from '../core/utils'
import { useQueryClient } from './QueryClientProvider'

export function useIsMutating(filters?: MutationFilters): number
interface Options {
context?: React.Context<QueryClient | undefined>
}

export function useIsMutating(
filters?: MutationFilters,
options?: Options
): number
export function useIsMutating(
queryKey?: QueryKey,
filters?: MutationFilters
filters?: MutationFilters,
options?: Options
): number
export function useIsMutating(
arg1?: QueryKey | MutationFilters,
arg2?: MutationFilters
arg2?: MutationFilters | Options,
arg3?: Options
): number {
const mountedRef = React.useRef(false)
const filters = parseMutationFilterArgs(arg1, arg2)
const [filters, options = {}] = parseMutationFilterArgs(arg1, arg2, arg3)

const queryClient = useQueryClient()
const queryClient = useQueryClient({ context: options.context })

const [isMutating, setIsMutating] = React.useState(
queryClient.isMutating(filters)
Expand Down
2 changes: 1 addition & 1 deletion src/react/useMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export function useMutation<
const [, forceUpdate] = React.useState(0)

const options = parseMutationArgs(arg1, arg2, arg3)
const queryClient = useQueryClient()
const queryClient = useQueryClient({ context: options.context })

const obsRef = React.useRef<MutationObserver<any, any, any, any>>()

Expand Down
13 changes: 12 additions & 1 deletion src/react/useQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,18 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] {
const mountedRef = React.useRef(false)
const [, forceUpdate] = React.useState(0)

const queryClient = useQueryClient()
// Ensure that all queries have the same context
queries.forEach((curr, i) => {
const next = queries[i + 1]
if (next && curr.context !== next.context) {
throw new Error(
'useQueries requires all queries to have the same context'
)
}
})

const context = queries[0]?.context
const queryClient = useQueryClient({ context })

const defaultedQueries = queries.map(options => {
const defaultedOptions = queryClient.defaultQueryObserverOptions(options)
Expand Down

0 comments on commit ed74e80

Please sign in to comment.