The notifyManager is a singleton that manages notification scheduling and batching for TanStack Query. It provides utilities to batch multiple state updates together and control how notifications are executed.
Import
import { notifyManager } from '@tanstack/query-core'
Methods
batch
Execute a callback and batch all notifications triggered during its execution into a single tick.
batch<T>(callback: () => T): T
A function to execute. All notifications triggered during this function’s execution will be batched and flushed together.
Returns the result of the callback function.
Example
import { notifyManager } from '@tanstack/query-core'
notifyManager.batch(() => {
// Multiple state updates here will be batched
queryClient.setQueryData('key1', data1)
queryClient.setQueryData('key2', data2)
queryClient.setQueryData('key3', data3)
// All subscribers will be notified in a single batch
})
This is useful for performance optimization when you need to make multiple updates at once. Instead of triggering re-renders for each update, they will all be batched together.
batchCalls
Wrap a function so that all calls to it are automatically batched.
batchCalls<T extends Array<unknown>>(
callback: (...args: T) => void
): (...args: T) => void
callback
(...args: T) => void
required
The function to wrap. All calls to the returned function will be batched and executed together.
Returns a wrapped version of the callback that batches all calls.
Example
import { notifyManager } from '@tanstack/query-core'
const updateData = notifyManager.batchCalls((id: string, data: unknown) => {
queryClient.setQueryData(id, data)
})
// These calls will be batched together
updateData('key1', data1)
updateData('key2', data2)
updateData('key3', data3)
schedule
Schedule a callback to be executed. If called within a batch, the callback will be queued and executed when the batch completes.
schedule(callback: () => void): void
The callback function to schedule.
Example
import { notifyManager } from '@tanstack/query-core'
notifyManager.schedule(() => {
console.log('This will be executed in the next tick')
})
setNotifyFunction
Set a custom function to wrap all notifications. This is useful for testing or integrating with framework-specific batching mechanisms.
setNotifyFunction(fn: (callback: () => void) => void): void
fn
(callback: () => void) => void
required
A function that will be called to execute each notification. It receives a callback that should be invoked to execute the actual notification.
This is commonly used to wrap notifications with React.act during testing or to integrate with React’s batching mechanism.
Example
import { notifyManager } from '@tanstack/query-core'
import { act } from '@testing-library/react'
// Wrap notifications with React.act for testing
notifyManager.setNotifyFunction((callback) => {
act(callback)
})
setBatchNotifyFunction
Set a custom function to batch multiple notifications together. By default, TanStack Query uses the batch function provided by ReactDOM or React Native.
setBatchNotifyFunction(fn: (callback: () => void) => void): void
fn
(callback: () => void) => void
required
A function that receives a callback containing multiple batched notifications. This function should execute the callback in a way that batches the updates together.
Example
import { notifyManager } from '@tanstack/query-core'
import { unstable_batchedUpdates } from 'react-dom'
// Use React's batching mechanism
notifyManager.setBatchNotifyFunction(unstable_batchedUpdates)
setScheduler
Set a custom scheduler function for executing notifications.
setScheduler(fn: (callback: () => void) => void): void
fn
(callback: () => void) => void
required
A function that schedules the callback to be executed. By default, this uses a zero-delay timeout (setTimeout with 0ms delay).
Example
import { notifyManager } from '@tanstack/query-core'
// Use requestAnimationFrame for scheduling
notifyManager.setScheduler((callback) => {
requestAnimationFrame(callback)
})
// Or use queueMicrotask for synchronous scheduling
notifyManager.setScheduler((callback) => {
queueMicrotask(callback)
})
Use Cases
Testing with React
When testing React components, you should wrap notifications with React.act to ensure proper batching:
import { notifyManager } from '@tanstack/query-core'
import { act } from '@testing-library/react'
beforeEach(() => {
notifyManager.setNotifyFunction((callback) => {
act(callback)
})
})
afterEach(() => {
// Reset to default
notifyManager.setNotifyFunction((callback) => {
callback()
})
})
Custom Batching Strategy
You can implement a custom batching strategy to control when updates are flushed:
import { notifyManager } from '@tanstack/query-core'
let pendingUpdates: Array<() => void> = []
let rafId: number | null = null
notifyManager.setBatchNotifyFunction((callback) => {
pendingUpdates.push(callback)
if (rafId === null) {
rafId = requestAnimationFrame(() => {
const updates = pendingUpdates
pendingUpdates = []
rafId = null
updates.forEach(update => update())
})
}
})
Use batch to group multiple updates and prevent unnecessary re-renders:
import { notifyManager, useQueryClient } from '@tanstack/react-query'
function updateMultipleQueries() {
const queryClient = useQueryClient()
// Without batching: 3 separate re-renders
queryClient.setQueryData('user', userData)
queryClient.setQueryData('posts', postsData)
queryClient.setQueryData('comments', commentsData)
// With batching: 1 re-render
notifyManager.batch(() => {
queryClient.setQueryData('user', userData)
queryClient.setQueryData('posts', postsData)
queryClient.setQueryData('comments', commentsData)
})
}
Synchronous Scheduling
In some cases, you may want notifications to execute synchronously:
import { notifyManager } from '@tanstack/query-core'
// Execute notifications synchronously
notifyManager.setScheduler((callback) => {
callback()
})
Be cautious with synchronous scheduling as it can lead to performance issues if many notifications are triggered in quick succession.
Default Behavior
By default, the notifyManager:
- Uses a zero-delay timeout (
setTimeout(callback, 0)) for scheduling notifications
- Executes notifications immediately (no wrapping)
- Executes batch notifications immediately (no framework-specific batching)
These defaults work well for most use cases, but you can customize them based on your framework and testing requirements.