Skip to content

Commit 74f2e28

Browse files
paulobmarcosTkDodo
andauthored
feat: add new function type to notifyOnChangeProps (#5734)
Co-authored-by: Dominik Dorfmeister <[email protected]>
1 parent 10e03a5 commit 74f2e28

File tree

5 files changed

+329
-88
lines changed

5 files changed

+329
-88
lines changed

docs/react/react-native.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,64 @@ export function useRefreshOnFocus<T>(refetch: () => Promise<T>) {
7676
```
7777

7878
In the above code, `refetch` is skipped the first time because `useFocusEffect` calls our callback on mount in addition to screen focus.
79+
80+
## Disable re-renders on out of focus Screens
81+
82+
In some situations, including performance concerns, you may want to stop re-renders when a React Native screen gets out of focus. To achieve this we can use `useFocusEffect` from `@react-navigation/native` together with the `notifyOnChangeProps` query option.
83+
84+
This custom hook provides a `notifyOnChangeProps` option that will return an empty array whenever a screen goes out of focus - effectively stopping any re-renders on that scenario. Whenever the screens gets in focus again, the behavior goes back to normal.
85+
86+
```tsx
87+
import React from 'react'
88+
import { NotifyOnChangeProps } from '@tanstack/query-core'
89+
import { useFocusEffect } from '@react-navigation/native'
90+
91+
export function useFocusNotifyOnChangeProps(notifyOnChangeProps?: NotifyOnChangeProps) {
92+
const focusedRef = React.useRef(true)
93+
94+
useFocusEffect(
95+
React.useCallback(() => {
96+
focusedRef.current = true
97+
98+
return () => {
99+
focusedRef.current = false
100+
}
101+
}, [])
102+
)
103+
104+
return () => {
105+
if (!focusedRef.current) {
106+
return []
107+
}
108+
109+
if (typeof notifyOnChangeProps === 'function') {
110+
return notifyOnChangeProps()
111+
}
112+
113+
return notifyOnChangeProps.current
114+
}
115+
}
116+
```
117+
118+
In the above code, `useFocusEffect` is used to change the value of a reference that the callback will use as a condition.
119+
120+
The argument is wrapped in a reference to also guarantee that the returned callback always keeps the same reference.
121+
122+
Example usage:
123+
124+
```tsx
125+
function MyComponent() {
126+
const notifyOnChangeProps = useFocusNotifyOnChangeProps();
127+
128+
const { dataUpdatedAt } = useQuery({
129+
queryKey: ['myKey'],
130+
queryFn: async () => {
131+
const response = await fetch('https://api.github.com/repos/tannerlinsley/react-query');
132+
return response.json();
133+
},
134+
notifyOnChangeProps,
135+
});
136+
137+
return <div>DataUpdatedAt: {dataUpdatedAt}</div>;
138+
};
139+
```

docs/react/reference/useQuery.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,12 @@ const {
131131
- If set to `false`, the query will not refetch on reconnect.
132132
- If set to `"always"`, the query will always refetch on reconnect.
133133
- If set to a function, the function will be executed with the query to compute the value
134-
- `notifyOnChangeProps: string[] | "all"`
134+
- `notifyOnChangeProps: string[] | "all" | (() => string[] | "all")`
135135
- Optional
136136
- If set, the component will only re-render if any of the listed properties change.
137137
- If set to `['data', 'error']` for example, the component will only re-render when the `data` or `error` properties change.
138138
- If set to `"all"`, the component will opt-out of smart tracking and re-render whenever a query is updated.
139+
- If set to a function, the function will be executed to compute the list of properties.
139140
- By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
140141
- `onSuccess: (data: TData) => void`
141142
- **Deprecated** - this callback will be removed in the next major version

packages/query-core/src/queryObserver.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,15 +637,21 @@ export class QueryObserver<
637637
}
638638

639639
const { notifyOnChangeProps } = this.options
640+
const notifyOnChangePropsValue =
641+
typeof notifyOnChangeProps === 'function'
642+
? notifyOnChangeProps()
643+
: notifyOnChangeProps
640644

641645
if (
642-
notifyOnChangeProps === 'all' ||
643-
(!notifyOnChangeProps && !this.trackedProps.size)
646+
notifyOnChangePropsValue === 'all' ||
647+
(!notifyOnChangePropsValue && !this.trackedProps.size)
644648
) {
645649
return true
646650
}
647651

648-
const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps)
652+
const includedProps = new Set(
653+
notifyOnChangePropsValue ?? this.trackedProps,
654+
)
649655

650656
if (this.options.useErrorBoundary) {
651657
includedProps.add('error')

packages/query-core/src/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ export interface QueryMeta {
5555

5656
export type NetworkMode = 'online' | 'always' | 'offlineFirst'
5757

58+
export type NotifyOnChangeProps =
59+
| Array<keyof InfiniteQueryObserverResult>
60+
| 'all'
61+
| (() => Array<keyof InfiniteQueryObserverResult> | 'all')
62+
5863
export interface QueryOptions<
5964
TQueryFnData = unknown,
6065
TError = unknown,
@@ -200,9 +205,10 @@ export interface QueryObserverOptions<
200205
* If set, the component will only re-render if any of the listed properties change.
201206
* When set to `['data', 'error']`, the component will only re-render when the `data` or `error` properties change.
202207
* When set to `'all'`, the component will re-render whenever a query is updated.
208+
* When set to a function, the function will be executed to compute the list of properties.
203209
* By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change.
204210
*/
205-
notifyOnChangeProps?: Array<keyof InfiniteQueryObserverResult> | 'all'
211+
notifyOnChangeProps?: NotifyOnChangeProps
206212
/**
207213
* This callback will fire any time the query successfully fetches new data.
208214
*

0 commit comments

Comments
 (0)