Skip to content

Commit 26600b1

Browse files
authored
feat: add structural sharing configuration flag (TanStack#902)
1 parent 799eb90 commit 26600b1

File tree

7 files changed

+40
-13
lines changed

7 files changed

+40
-13
lines changed

docs/src/pages/docs/api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const {
3333
refetchIntervalInBackground,
3434
queryFnParamsFilter,
3535
refetchOnMount,
36+
structuralSharing,
3637
isDataEqual,
3738
onError,
3839
onSuccess,
@@ -125,6 +126,10 @@ const queryInfo = useQuery({
125126
- Optional
126127
- This function will filter the params that get passed to `queryFn`.
127128
- For example, you can filter out the first query key from the params by using `queryFnParamsFilter: args => args.slice(1)`.
129+
- `structuralSharing: Boolean`
130+
- Optional
131+
- Defaults to `true`
132+
- If set to `false`, structural sharing between query results will be disabled.
128133
129134
**Returns**
130135

docs/src/pages/docs/guides/important-defaults.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Out of the box, React Query is configured with **aggressive but sane** defaults.
99
- Query results that become unused (all instances of the query are unmounted) will still be cached in case they are used again for a default of 5 minutes before they are garbage collected. To change this, you can alter the default `cacheTime` for queries to something other than `1000 * 60 * 5` milliseconds.
1010
- Stale queries will automatically be refetched in the background **when the browser window is refocused by the user**. You can disable this using the `refetchOnWindowFocus` option in queries or the global config.
1111
- Queries that fail will silently be retried **3 times, with exponential backoff delay** before capturing and displaying an error to the UI. To change this, you can alter the default `retry` and `retryDelay` options for queries to something other than `3` and the default exponential backoff function.
12-
- Query results by default are deep compared to detect if data has actually changed and if not, the data reference remains unchanged to better help with value stabilization with regards to useMemo and useCallback. The default deep compare function use here (`config.isDataEqual`) only supports comparing JSON-compatible primitives. If you are dealing with any non-json compatible values in your query responses OR are seeing performance issues with the deep compare function, you should probably disable it (`config.isDataEqual = () => false`) or customize it to better fit your needs.
12+
- Query results by default are structurally shared to detect if data has actually changed and if not, the data reference remains unchanged to better help with value stabilization with regards to useMemo and useCallback. Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can define a data compare function with `config.isDataEqual`.

docs/src/pages/docs/typescript.md

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@ title: TypeScript
55

66
React Query is now written in **TypeScript** to make sure the library and your projects are type-safe!
77

8-
## Migration
9-
10-
React Query is currently typed with an external type definition file, which unfortunately often gets out of sync with the actual code.
11-
12-
This is one of the reasons why the library has been migrated to TypeScript.
13-
14-
But before exposing the new types, we first want to get your feedback on it!
15-
16-
Install the `tsnext` tag to get the latest React Query with the new types:
8+
Install the latest version to get React Query with the new types:
179

1810
```sh
19-
npm install react-query@tsnext --save
11+
npm install react-query --save
2012
```
2113

2214
## Changes

src/core/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const DEFAULT_CONFIG: ReactQueryConfig = {
5757
cacheTime: 5 * 60 * 1000,
5858
refetchOnWindowFocus: true,
5959
refetchOnMount: true,
60+
structuralSharing: true,
6061
},
6162
}
6263

src/core/query.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,10 @@ export class Query<TResult, TError> {
289289
// Get the new data
290290
let data: TResult | undefined = functionalUpdate(updater, prevData)
291291

292-
// Structurally share data between prev and new data
293-
data = replaceEqualDeep(prevData, data)
292+
// Structurally share data between prev and new data if needed
293+
if (this.config.structuralSharing) {
294+
data = replaceEqualDeep(prevData, data)
295+
}
294296

295297
// Use prev data if an isDataEqual function is defined and returns `true`
296298
if (this.config.isDataEqual?.(prevData, data)) {

src/core/tests/queryCache.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,28 @@ describe('queryCache', () => {
8585
).rejects.toEqual(new Error('error'))
8686
})
8787

88+
test('prefetchQuery should return undefined when an error is thrown', async () => {
89+
const key = queryKey()
90+
91+
const consoleMock = jest.spyOn(console, 'error')
92+
consoleMock.mockImplementation(() => undefined)
93+
94+
const result = await defaultQueryCache.prefetchQuery(
95+
key,
96+
async () => {
97+
throw new Error('error')
98+
},
99+
{
100+
retry: false,
101+
}
102+
)
103+
104+
expect(result).toBeUndefined()
105+
expect(consoleMock).toHaveBeenCalled()
106+
107+
consoleMock.mockRestore()
108+
})
109+
88110
test('should notify listeners when new query is added', async () => {
89111
const key = queryKey()
90112

src/core/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export interface BaseQueryConfig<TResult, TError = unknown> {
5757
initialData?: TResult | InitialDataFunction<TResult>
5858
initialStale?: boolean | InitialStaleFunction
5959
infinite?: true
60+
/**
61+
* Set this to `false` to disable structural sharing between query results.
62+
* Defaults to `true`.
63+
*/
64+
structuralSharing?: boolean
6065
}
6166

6267
export interface QueryObserverConfig<TResult, TError = unknown>

0 commit comments

Comments
 (0)