Skip to content

Commit 5b464da

Browse files
feat(QueryObserver): track queries as default (#2987)
* feat(Query Options): remove notifyOnChangePropsExclusion - remove related code from queryObserver - remove type def - remove related tests * docs(Query Options): update notifyOnChangePropsExclusion sections - remove from api references - add to v4 migration guide * feat(QueryObserver): "tracked" as default behavior - remove "tracked" completely if notifyOnChangeProps is not defined, behave as v3 "tracked" - add `notifyOnChangeProps: 'all' to opt out of the smart tracking TODO: Now that default behavior has changed, work out the failed tests. Which parts to change for current ones and possibly write new ones. * test(useQuery): adjust tests to pass for notifyOnChangeProps udpate * test(useInfiniteQuery): adjust tests to pass for notifyOnChangeProps udpate * test(QueryResetErrorBoundary): adjust tests to pass for notifyOnChangeProps udpate * refactor(QueryObserver): use nullish coalescing operator much cleaner than the negated if I started with * test(QueryResetErrorBoundary): remove "tracked" from test * revert: test(QueryResetErrorBoundary): adjust tests to pass for notifyOnChaneProps udpate This reverts commit a34b472. The changes are not necessary after PR #2993 fix. * refactor(QueryObserver): combine prop checks * docs(notifyOnChangeProps): update docs to reflect new api
1 parent 67b9a9b commit 5b464da

File tree

10 files changed

+70
-121
lines changed

10 files changed

+70
-121
lines changed

docs/src/pages/comparison.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Feature/Capability Key:
6464

6565
> **<sup>1</sup> Lagged Query Data** - React Query provides a way to continue to see an existing query's data while the next query loads (similar to the same UX that suspense will soon provide natively). This is extremely important when writing pagination UIs or infinite loading UIs where you do not want to show a hard loading state whenever a new query is requested. Other libraries do not have this capability and render a hard loading state for the new query (unless it has been prefetched), while the new query loads.
6666
67-
> **<sup>2</sup> Render Optimization** - React Query has excellent rendering performance. It will only re-render your components when a query is updated. For example because it has new data, or to indicate it is fetching. React Query also batches updates together to make sure your application only re-renders once when multiple components are using the same query. If you are only interested in the `data` or `error` properties, you can reduce the number of renders even more by setting `notifyOnChangeProps` to `['data', 'error']`. Set `notifyOnChangeProps: 'tracked'` to automatically track which fields are accessed and only re-render if one of them changes.
67+
> **<sup>2</sup> Render Optimization** - React Query has excellent rendering performance. By default, it will automatically track which fields are accessed and only re-render if one of them changes. If you would like to opt-out of this optimization, setting `notifyOnChangeProps` to `'all'` will re-render your components whenever the query is updated. For example because it has new data, or to indicate it is fetching. React Query also batches updates together to make sure your application only re-renders once when multiple components are using the same query. If you are only interested in the `data` or `error` properties, you can reduce the number of renders even more by setting `notifyOnChangeProps` to `['data', 'error']`.
6868
6969
> **<sup>3</sup> Partial query matching** - Because React Query uses deterministic query key serialization, this allows you to manipulate variable groups of queries without having to know each individual query-key that you want to match, eg. you can refetch every query that starts with `todos` in its key, regardless of variables, or you can target specific queries with (or without) variables or nested properties, and even use a filter function to only match queries that pass your specific conditions.
7070

docs/src/pages/guides/migrating-to-react-query-4.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ With version [3.22.0](https://github.com/tannerlinsley/react-query/releases/tag/
2727
+ import { dehydrate, hydrate, useHydrate, Hydrate } from 'react-query'
2828
```
2929

30+
### `notifyOnChangeProps` property no longer accepts `"tracked"` as a value
31+
32+
The `notifyOnChangeProps` option no longer accepts a `"tracked"` value. Instead, `useQuery` defaults to tracking properties. All queries using `notifyOnChangeProps: "tracked"` should be updated by removing this option.
33+
34+
If you would like to bypass this in any queries to emulate the v3 default behavior of re-rendering whenever a query changes, `notifyOnChangeProps` now accepts an `"all"` value to opt-out of the default smart tracking optimization.
35+
36+
### `notifyOnChangePropsExclusion` has been removed
37+
38+
In v4, `notifyOnChangeProps` defaults to the `"tracked"` behavior of v3 instead of `undefined`. Now that `"tracked"` is the default behavior for v4, it no longer makes sense to include this config option.
39+
3040
### Consistent behavior for `cancelRefetch`
3141

3242
The `cancelRefetch` can be passed to all functions that imperatively fetch a query, namely:

docs/src/pages/reference/QueryClient.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ try {
9191

9292
**Options**
9393

94-
The options for `fetchQuery` are exactly the same as those of [`useQuery`](./useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, notifyOnChangePropsExclusions, onSuccess, onError, onSettled, useErrorBoundary, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity.
94+
The options for `fetchQuery` are exactly the same as those of [`useQuery`](./useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, useErrorBoundary, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity.
9595

9696
**Returns**
9797

docs/src/pages/reference/useQuery.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ const {
3535
keepPreviousData,
3636
meta,
3737
notifyOnChangeProps,
38-
notifyOnChangePropsExclusions,
3938
onError,
4039
onSettled,
4140
onSuccess,
@@ -125,15 +124,12 @@ const result = useQuery({
125124
- If set to `true`, the query will refetch on reconnect if the data is stale.
126125
- If set to `false`, the query will not refetch on reconnect.
127126
- If set to `"always"`, the query will always refetch on reconnect.
128-
- `notifyOnChangeProps: string[] | "tracked"`
127+
- `notifyOnChangeProps: string[] | "all"`
129128
- Optional
130129
- If set, the component will only re-render if any of the listed properties change.
131130
- If set to `['data', 'error']` for example, the component will only re-render when the `data` or `error` properties change.
132-
- If set to `"tracked"`, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
133-
- `notifyOnChangePropsExclusions: string[]`
134-
- Optional
135-
- If set, the component will not re-render if any of the listed properties change.
136-
- If set to `['isStale']` for example, the component will not re-render when the `isStale` property changes.
131+
- If set to `"all"`, the component will opt-out of smart tracking and re-render whenever a query is updated.
132+
- By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
137133
- `onSuccess: (data: TData) => void`
138134
- Optional
139135
- This function will fire any time the query successfully fetches new data.

src/core/queryObserver.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -610,27 +610,22 @@ export class QueryObserver<
610610
return true
611611
}
612612

613-
const { notifyOnChangeProps, notifyOnChangePropsExclusions } = this.options
613+
const { notifyOnChangeProps } = this.options
614614

615-
if (!notifyOnChangeProps && !notifyOnChangePropsExclusions) {
616-
return true
617-
}
618-
619-
if (notifyOnChangeProps === 'tracked' && !this.trackedProps.length) {
615+
if (
616+
notifyOnChangeProps === 'all' ||
617+
(!notifyOnChangeProps && !this.trackedProps.length)
618+
) {
620619
return true
621620
}
622621

623-
const includedProps =
624-
notifyOnChangeProps === 'tracked'
625-
? this.trackedProps
626-
: notifyOnChangeProps
622+
const includedProps = notifyOnChangeProps ?? this.trackedProps
627623

628624
return Object.keys(result).some(key => {
629625
const typedKey = key as keyof QueryObserverResult
630626
const changed = result[typedKey] !== prevResult[typedKey]
631627
const isIncluded = includedProps?.some(x => x === key)
632-
const isExcluded = notifyOnChangePropsExclusions?.some(x => x === key)
633-
return changed && !isExcluded && (!includedProps || isIncluded)
628+
return changed && (!includedProps || isIncluded)
634629
})
635630
}
636631

src/core/types.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,10 @@ export interface QueryObserverOptions<
154154
/**
155155
* If set, the component will only re-render if any of the listed properties change.
156156
* When set to `['data', 'error']`, the component will only re-render when the `data` or `error` properties change.
157-
* When set to `tracked`, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
157+
* When set to `'all'`, the component will re-render whenever a query is updated.
158+
* By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
158159
*/
159-
notifyOnChangeProps?: Array<keyof InfiniteQueryObserverResult> | 'tracked'
160-
/**
161-
* If set, the component will not re-render if any of the listed properties change.
162-
*/
163-
notifyOnChangePropsExclusions?: Array<keyof InfiniteQueryObserverResult>
160+
notifyOnChangeProps?: Array<keyof InfiniteQueryObserverResult> | 'all'
164161
/**
165162
* This callback will fire any time the query successfully fetches new data or the cache is updated via `setQueryData`.
166163
*/

src/reactjs/tests/QueryResetErrorBoundary.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,6 @@ describe('QueryErrorResetBoundary', () => {
613613
{
614614
retry: false,
615615
useErrorBoundary: true,
616-
notifyOnChangeProps: 'tracked',
617616
}
618617
)
619618
return <div>{data}</div>

src/reactjs/tests/useInfiniteQuery.test.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ describe('useInfiniteQuery', () => {
186186
{
187187
getNextPageParam: () => 1,
188188
keepPreviousData: true,
189+
notifyOnChangeProps: 'all',
189190
}
190191
)
191192

@@ -306,6 +307,7 @@ describe('useInfiniteQuery', () => {
306307
pages: [...data.pages].reverse(),
307308
pageParams: [...data.pageParams].reverse(),
308309
}),
310+
notifyOnChangeProps: 'all',
309311
}
310312
)
311313

@@ -359,6 +361,7 @@ describe('useInfiniteQuery', () => {
359361
},
360362
{
361363
getPreviousPageParam: firstPage => firstPage - 1,
364+
notifyOnChangeProps: 'all',
362365
}
363366
)
364367

@@ -423,8 +426,10 @@ describe('useInfiniteQuery', () => {
423426
const states: UseInfiniteQueryResult<number>[] = []
424427

425428
function Page() {
426-
const state = useInfiniteQuery(key, ({ pageParam = 10 }) =>
427-
Number(pageParam)
429+
const state = useInfiniteQuery(
430+
key,
431+
({ pageParam = 10 }) => Number(pageParam),
432+
{ notifyOnChangeProps: 'all' }
428433
)
429434

430435
states.push(state)
@@ -516,6 +521,7 @@ describe('useInfiniteQuery', () => {
516521
{
517522
getPreviousPageParam: firstPage => firstPage - 1,
518523
getNextPageParam: lastPage => lastPage + 1,
524+
notifyOnChangeProps: 'all',
519525
}
520526
)
521527

@@ -608,6 +614,7 @@ describe('useInfiniteQuery', () => {
608614
({ pageParam = 10 }) => Number(pageParam) * multiplier.current,
609615
{
610616
getNextPageParam: lastPage => lastPage + 1,
617+
notifyOnChangeProps: 'all',
611618
}
612619
)
613620

@@ -687,6 +694,7 @@ describe('useInfiniteQuery', () => {
687694
},
688695
{
689696
getNextPageParam: lastPage => lastPage + 1,
697+
notifyOnChangeProps: 'all',
690698
}
691699
)
692700

@@ -913,6 +921,7 @@ describe('useInfiniteQuery', () => {
913921
},
914922
{
915923
getNextPageParam: lastPage => lastPage + 1,
924+
notifyOnChangeProps: 'all',
916925
}
917926
)
918927

@@ -1014,6 +1023,7 @@ describe('useInfiniteQuery', () => {
10141023
},
10151024
{
10161025
getNextPageParam: lastPage => lastPage + 1,
1026+
notifyOnChangeProps: 'all',
10171027
}
10181028
)
10191029

@@ -1080,6 +1090,7 @@ describe('useInfiniteQuery', () => {
10801090
},
10811091
{
10821092
getNextPageParam: lastPage => lastPage + 1,
1093+
notifyOnChangeProps: 'all',
10831094
}
10841095
)
10851096

@@ -1169,6 +1180,7 @@ describe('useInfiniteQuery', () => {
11691180
{
11701181
initialData: { pages: [1], pageParams: [1] },
11711182
getNextPageParam: lastPage => lastPage + 1,
1183+
notifyOnChangeProps: 'all',
11721184
}
11731185
)
11741186

0 commit comments

Comments
 (0)