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

Support the ability to provide a context #2548

Merged
merged 2 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 0 additions & 8 deletions .vscode/settings.json

This file was deleted.

3 changes: 3 additions & 0 deletions docs/src/pages/devtools.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ReactQueryDevtools } from 'react-query/devtools'
```

By default, React Query Devtools are only included in bundles when `process.env.NODE_ENV === 'development'`, so you don't need to worry about excluding them during a production build.

## Floating Mode

Floating Mode will mount the devtools as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads.
Expand Down Expand Up @@ -50,6 +51,8 @@ function App() {
- `position?: "top-left" | "top-right" | "bottom-left" | "bottom-right"`
- Defaults to `bottom-left`
- The position of the React Query logo to open and close the devtools panel
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

## Embedded Mode

Expand Down
87 changes: 87 additions & 0 deletions docs/src/pages/guides/migrating-to-react-query-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,90 @@ When using the [functional updater form of setQueryData](../reference/QueryClien
(previousTodo) => previousTodo ? { ...previousTodo, done: true } : undefined
)
```

### Custom Contexts for Multiple Providers

Custom contexts can now be specified to pair hooks with their matching `Provider`. This is critical when there may be multiple React Query `Provider` instances in the component tree and you need to ensure your hook uses the correct `Provider` instance.

An example:

1) Create a data package.

```tsx
// Our first data package: @my-scope/container-data

const context = React.createContext<QueryClient | undefined>();
const queryCache = new QueryCache()
const queryClient = new QueryClient({ queryCache, context })


export const useUser = () => {
return useQuery(USER_KEY, USER_FETCHER, {
context,
})
}

export const ContainerDataProvider: React.FC = ({ children }) => {
return (
<QueryClientProvider client={queryClient} context={context}>
{children}
</QueryClientProvider>
);
}

```

2) Create a second data package.

```tsx
// Our second data package: @my-scope/my-component-data

const context = React.createContext<QueryClient | undefined>();
const queryCache = new QueryCache()
const queryClient = new QueryClient({ queryCache, context })


export const useItems = () => {
return useQuery(ITEMS_KEY, ITEMS_FETCHER, {
context,
})
}

export const MyComponentDataProvider: React.FC = ({ children }) => {
return (
<QueryClientProvider client={queryClient} context={context}>
{children}
</QueryClientProvider>
);
}

```

3) Use these two data packages in your application.

```tsx
// Our application

import { ContainerDataProvider, useUser } from "@my-scope/container-data";
import { AppDataProvider } from "@my-scope/app-data";
import { MyComponentDataProvider, useItems } from "@my-scope/my-component-data";

<ContainerDataProvider> // <-- Provides container data (like "user") using its own React Query provider
...
<AppDataProvider> // <-- Provides app data using its own React Query provider (unused in this example)
...
<MyComponentDataProvider> // <-- Provides component data (like "items") using its own React Query provider
<MyComponent />
</MyComponentDataProvider>
...
</AppDataProvider>
...
</ContainerDataProvider>

// Example of hooks provided by the "DataProvider" components above:
const MyComponent = () => {
const user = useUser(); // <-- Uses the context specified in ContainerDataProvider.
const items = useItems(); // <-- Uses the context specified in MyComponentDataProvider
...
}
```
5 changes: 4 additions & 1 deletion docs/src/pages/reference/QueryClientProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
```

**Options**

- `client: QueryClient`
- **Required**
- the QueryClient instance to provide
- `contextSharing: boolean`
- defaults to `false`
- Set this to `true` to enable context sharing, which will 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.
- Set this to `true` to enable context sharing, which will 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.
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.
6 changes: 6 additions & 0 deletions docs/src/pages/reference/hydration.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ hydrate(queryClient, dehydratedState, options)
- Optional
- `mutations: MutationOptions` The default mutation options to use for the hydrated mutations.
- `queries: QueryOptions` The default query options to use for the hydrated queries.
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

### Limitations

Expand All @@ -108,6 +110,8 @@ useHydrate(dehydratedState, options)
- Optional
- `defaultOptions: QueryOptions`
- The default query options to use for the hydrated queries.
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

## `Hydrate`

Expand All @@ -129,3 +133,5 @@ function App() {
- Optional
- `defaultOptions: QueryOptions`
- The default query options to use for the hydrated queries.
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.
2 changes: 2 additions & 0 deletions docs/src/pages/reference/useIsFetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const isFetchingPosts = useIsFetching(['posts'])

- `queryKey?: QueryKey`: [Query Keys](../guides/query-keys)
- `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters)
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

**Returns**

Expand Down
2 changes: 2 additions & 0 deletions docs/src/pages/reference/useIsMutating.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const isMutatingPosts = useIsMutating(['posts'])

- `mutationKey?: string | unknown[]`
- `filters?: MutationFilters`: [Mutation Filters](../guides/filters#mutation-filters)
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

**Returns**

Expand Down
2 changes: 2 additions & 0 deletions docs/src/pages/reference/useMutation.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ mutate(variables, {
- `meta: Record<string, unknown>`
- Optional
- If set, stores additional information on the mutation cache entry that can be used as needed. It will be accessible wherever the `mutation` is available (eg. `onError`, `onSuccess` functions of the `MutationCache`).
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

**Returns**

Expand Down
5 changes: 4 additions & 1 deletion docs/src/pages/reference/useQueries.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const results = useQueries({

**Options**

The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery).
The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery) (excluding the `context` option).

- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

**Returns**

Expand Down
2 changes: 2 additions & 0 deletions docs/src/pages/reference/useQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ const result = useQuery({
- `meta: Record<string, unknown>`
- Optional
- If set, stores additional information on the query cache entry that can be used as needed. It will be accessible wherever the `query` is available, and is also part of the `QueryFunctionContext` provided to the `queryFn`.
- `context?: React.Context<QueryClient | undefined>`
- Use this to use a custom React Query context. Otherwise, `defaultContext` will be used.

**Returns**

Expand Down
3 changes: 2 additions & 1 deletion src/core/hydration.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ContextOptions } from '../reactjs/types'
import type { QueryClient } from './queryClient'
import type { Query, QueryState } from './query'
import type {
Expand All @@ -17,7 +18,7 @@ export interface DehydrateOptions {
shouldDehydrateQuery?: ShouldDehydrateQueryFunction
}

export interface HydrateOptions {
export interface HydrateOptions extends ContextOptions {
defaultOptions?: {
queries?: QueryOptions
mutations?: MutationOptions
Expand Down
16 changes: 11 additions & 5 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,17 @@ export function parseFilterArgs<
: [arg1 || {}, arg2]) as [TFilters, TOptions]
}

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

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

import { Query, useQueryClient, onlineManager } from 'react-query'
import {
Query,
ContextOptions,
useQueryClient,
onlineManager,
} from 'react-query'
import { matchSorter } from 'match-sorter'
import useLocalStorage from './useLocalStorage'
import { useIsMounted, useSafeState } from './utils'
Expand All @@ -21,7 +26,7 @@ import Explorer from './Explorer'
import Logo from './Logo'
import { noop } from '../core/utils'

interface DevtoolsOptions {
interface DevtoolsOptions extends ContextOptions {
/**
* Set this true if you want the dev tools to default to being open
*/
Expand Down Expand Up @@ -60,7 +65,7 @@ interface DevtoolsOptions {
containerElement?: string | any
}

interface DevtoolsPanelOptions {
interface DevtoolsPanelOptions extends ContextOptions {
/**
* The standard React style object used to style a component with inline styles
*/
Expand Down Expand Up @@ -92,6 +97,7 @@ export function ReactQueryDevtools({
toggleButtonProps = {},
position = 'bottom-left',
containerElement: Container = 'aside',
context,
}: DevtoolsOptions): React.ReactElement | null {
const rootRef = React.useRef<HTMLDivElement>(null)
const panelRef = React.useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -229,6 +235,7 @@ export function ReactQueryDevtools({
<ThemeProvider theme={theme}>
<ReactQueryDevtoolsPanel
ref={panelRef as any}
context={context}
{...otherPanelProps}
style={{
position: 'fixed',
Expand Down Expand Up @@ -381,9 +388,15 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
HTMLDivElement,
DevtoolsPanelOptions
>(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
const { isOpen = true, setIsOpen, handleDragStart, ...panelProps } = props

const queryClient = useQueryClient()
const {
isOpen = true,
setIsOpen,
handleDragStart,
context,
...panelProps
} = props

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

const [sort, setSort] = useLocalStorage(
Expand Down
Loading