Framework
Version
Debouncer API Reference
Throttler API Reference
Rate Limiter API Reference
Queue API Reference
Batcher API Reference

React Example: UseBatchedCallback

tsx
import { useState } from 'react'
import ReactDOM from 'react-dom/client'
import { useBatchedCallback } from '@tanstack/react-pacer/batcher'

interface LogEntry {
  id: number
  message: string
  timestamp: Date
}

function App1() {
  const [logs, setLogs] = useState<LogEntry[]>([])
  const [logCount, setLogCount] = useState(0)

  // Create batched logger function - Stable reference provided by useBatchedCallback
  const batchedLogger = useBatchedCallback(
    (entries: LogEntry[]) => {
      console.log('Processing batch of logs:', entries)
      setLogs((current) => [...current, ...entries])
    },
    {
      maxSize: 3, // Process when 3 logs collected
      wait: 2000, // Or after 2 seconds
    },
  )

  function addLog(message: string) {
    const newLog: LogEntry = {
      id: Date.now() + Math.random(),
      message,
      timestamp: new Date(),
    }
    setLogCount((c) => c + 1)
    batchedLogger(newLog)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 1</h1>
      <div style={{ marginBottom: '20px' }}>
        <button onClick={() => addLog(`Log entry ${logCount + 1}`)}>
          Add Log Entry
        </button>
        <button
          onClick={() => addLog(`Warning ${logCount + 1}`)}
          style={{ marginLeft: '10px' }}
        >
          Add Warning
        </button>
        <button
          onClick={() => addLog(`Error ${logCount + 1}`)}
          style={{ marginLeft: '10px' }}
        >
          Add Error
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Logs Created:</td>
            <td>{logCount}</td>
          </tr>
          <tr>
            <td>Logs Processed:</td>
            <td>{logs.length}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px' }}>
        <h3>Processed Logs:</h3>
        <div
          style={{
            maxHeight: '200px',
            overflowY: 'auto',
            border: '1px solid #ccc',
            padding: '10px',
          }}
        >
          {logs.length === 0 ? (
            <p style={{ color: '#666' }}>No logs processed yet...</p>
          ) : (
            logs.map((log) => (
              <div
                key={log.id}
                style={{ marginBottom: '5px', fontSize: '0.9em' }}
              >
                <strong>{log.timestamp.toLocaleTimeString()}</strong>:{' '}
                {log.message}
              </div>
            ))
          )}
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        Logs are batched - max 3 items or 2 second wait time
      </p>
    </div>
  )
}

interface AnalyticsEvent {
  type: string
  target: string
  timestamp: Date
}

function App2() {
  const [events, setEvents] = useState<AnalyticsEvent[]>([])
  const [totalEvents, setTotalEvents] = useState(0)
  const [batchesProcessed, setBatchesProcessed] = useState(0)

  // Create batched analytics tracker - Stable reference provided by useBatchedCallback
  const trackEvents = useBatchedCallback(
    (events: AnalyticsEvent[]) => {
      console.log('Sending analytics batch:', events)
      setEvents((current) => [...current, ...events])
      setBatchesProcessed((count) => count + 1)
    },
    {
      maxSize: 5, // Send when 5 events collected
      wait: 3000, // Or after 3 seconds
    },
  )

  function trackEvent(type: string, target: string) {
    const event: AnalyticsEvent = {
      type,
      target,
      timestamp: new Date(),
    }
    setTotalEvents((count) => count + 1)
    trackEvents(event)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 2</h1>
      <div style={{ marginBottom: '20px' }}>
        <button onClick={() => trackEvent('click', 'button-1')}>
          Track Button Click
        </button>
        <button
          onClick={() => trackEvent('hover', 'card')}
          style={{ marginLeft: '10px' }}
        >
          Track Hover Event
        </button>
        <button
          onClick={() => trackEvent('view', 'page')}
          style={{ marginLeft: '10px' }}
        >
          Track Page View
        </button>
        <button
          onClick={() => trackEvent('form', 'submit')}
          style={{ marginLeft: '10px' }}
        >
          Track Form Submit
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Events Created:</td>
            <td>{totalEvents}</td>
          </tr>
          <tr>
            <td>Events Sent:</td>
            <td>{events.length}</td>
          </tr>
          <tr>
            <td>Batches Processed:</td>
            <td>{batchesProcessed}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px' }}>
        <h3>Sent Analytics Events:</h3>
        <div
          style={{
            maxHeight: '200px',
            overflowY: 'auto',
            border: '1px solid #ccc',
            padding: '10px',
          }}
        >
          {events.length === 0 ? (
            <p style={{ color: '#666' }}>No events sent yet...</p>
          ) : (
            events.map((event, index) => (
              <div
                key={index}
                style={{ marginBottom: '5px', fontSize: '0.9em' }}
              >
                <strong>{event.timestamp.toLocaleTimeString()}</strong>:{' '}
                {event.type} - {event.target}
              </div>
            ))
          )}
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        Analytics events are batched - max 5 events or 3 second wait time
      </p>
    </div>
  )
}

interface ApiRequest {
  id: string
  data: any
}

function App3() {
  const [requests, setRequests] = useState<ApiRequest[]>([])
  const [totalRequests, setTotalRequests] = useState(0)
  const [processedRequests, setProcessedRequests] = useState<ApiRequest[]>([])

  // Create batched API request handler - Stable reference provided by useBatchedCallback
  const batchApiRequests = useBatchedCallback(
    (requests: ApiRequest[]) => {
      console.log('Processing batch of API requests:', requests)
      // Simulate API processing
      setProcessedRequests((current) => [...current, ...requests])
    },
    {
      maxSize: 4, // Process when 4 requests collected
      wait: 1500, // Or after 1.5 seconds
    },
  )

  function makeApiRequest(data: any) {
    const request: ApiRequest = {
      id: `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      data,
    }
    setTotalRequests((count) => count + 1)
    setRequests((current) => [...current, request])
    batchApiRequests(request)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 3</h1>
      <div style={{ marginBottom: '20px' }}>
        <button
          onClick={() => makeApiRequest({ action: 'save', item: 'document' })}
        >
          Save Document
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'update', item: 'profile' })}
          style={{ marginLeft: '10px' }}
        >
          Update Profile
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'delete', item: 'file' })}
          style={{ marginLeft: '10px' }}
        >
          Delete File
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'create', item: 'folder' })}
          style={{ marginLeft: '10px' }}
        >
          Create Folder
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Requests Made:</td>
            <td>{totalRequests}</td>
          </tr>
          <tr>
            <td>Requests Queued:</td>
            <td>{requests.length - processedRequests.length}</td>
          </tr>
          <tr>
            <td>Requests Processed:</td>
            <td>{processedRequests.length}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px', display: 'flex', gap: '20px' }}>
        <div style={{ flex: 1 }}>
          <h3>Queued Requests:</h3>
          <div
            style={{
              maxHeight: '150px',
              overflowY: 'auto',
              border: '1px solid #ccc',
              padding: '10px',
            }}
          >
            {requests.filter(
              (req) => !processedRequests.some((p) => p.id === req.id),
            ).length === 0 ? (
              <p style={{ color: '#666' }}>No requests queued...</p>
            ) : (
              requests
                .filter(
                  (req) => !processedRequests.some((p) => p.id === req.id),
                )
                .map((request) => (
                  <div
                    key={request.id}
                    style={{ marginBottom: '5px', fontSize: '0.9em' }}
                  >
                    {request.id}: {JSON.stringify(request.data)}
                  </div>
                ))
            )}
          </div>
        </div>

        <div style={{ flex: 1 }}>
          <h3>Processed Requests:</h3>
          <div
            style={{
              maxHeight: '150px',
              overflowY: 'auto',
              border: '1px solid #ccc',
              padding: '10px',
            }}
          >
            {processedRequests.length === 0 ? (
              <p style={{ color: '#666' }}>No requests processed yet...</p>
            ) : (
              processedRequests.map((request) => (
                <div
                  key={request.id}
                  style={{ marginBottom: '5px', fontSize: '0.9em' }}
                >
                  {request.id}: {JSON.stringify(request.data)}
                </div>
              ))
            )}
          </div>
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        API requests are batched - max 4 requests or 1.5 second wait time
      </p>
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(
  <div>
    <App1 />
    <hr />
    <App2 />
    <hr />
    <App3 />
  </div>,
)
import { useState } from 'react'
import ReactDOM from 'react-dom/client'
import { useBatchedCallback } from '@tanstack/react-pacer/batcher'

interface LogEntry {
  id: number
  message: string
  timestamp: Date
}

function App1() {
  const [logs, setLogs] = useState<LogEntry[]>([])
  const [logCount, setLogCount] = useState(0)

  // Create batched logger function - Stable reference provided by useBatchedCallback
  const batchedLogger = useBatchedCallback(
    (entries: LogEntry[]) => {
      console.log('Processing batch of logs:', entries)
      setLogs((current) => [...current, ...entries])
    },
    {
      maxSize: 3, // Process when 3 logs collected
      wait: 2000, // Or after 2 seconds
    },
  )

  function addLog(message: string) {
    const newLog: LogEntry = {
      id: Date.now() + Math.random(),
      message,
      timestamp: new Date(),
    }
    setLogCount((c) => c + 1)
    batchedLogger(newLog)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 1</h1>
      <div style={{ marginBottom: '20px' }}>
        <button onClick={() => addLog(`Log entry ${logCount + 1}`)}>
          Add Log Entry
        </button>
        <button
          onClick={() => addLog(`Warning ${logCount + 1}`)}
          style={{ marginLeft: '10px' }}
        >
          Add Warning
        </button>
        <button
          onClick={() => addLog(`Error ${logCount + 1}`)}
          style={{ marginLeft: '10px' }}
        >
          Add Error
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Logs Created:</td>
            <td>{logCount}</td>
          </tr>
          <tr>
            <td>Logs Processed:</td>
            <td>{logs.length}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px' }}>
        <h3>Processed Logs:</h3>
        <div
          style={{
            maxHeight: '200px',
            overflowY: 'auto',
            border: '1px solid #ccc',
            padding: '10px',
          }}
        >
          {logs.length === 0 ? (
            <p style={{ color: '#666' }}>No logs processed yet...</p>
          ) : (
            logs.map((log) => (
              <div
                key={log.id}
                style={{ marginBottom: '5px', fontSize: '0.9em' }}
              >
                <strong>{log.timestamp.toLocaleTimeString()}</strong>:{' '}
                {log.message}
              </div>
            ))
          )}
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        Logs are batched - max 3 items or 2 second wait time
      </p>
    </div>
  )
}

interface AnalyticsEvent {
  type: string
  target: string
  timestamp: Date
}

function App2() {
  const [events, setEvents] = useState<AnalyticsEvent[]>([])
  const [totalEvents, setTotalEvents] = useState(0)
  const [batchesProcessed, setBatchesProcessed] = useState(0)

  // Create batched analytics tracker - Stable reference provided by useBatchedCallback
  const trackEvents = useBatchedCallback(
    (events: AnalyticsEvent[]) => {
      console.log('Sending analytics batch:', events)
      setEvents((current) => [...current, ...events])
      setBatchesProcessed((count) => count + 1)
    },
    {
      maxSize: 5, // Send when 5 events collected
      wait: 3000, // Or after 3 seconds
    },
  )

  function trackEvent(type: string, target: string) {
    const event: AnalyticsEvent = {
      type,
      target,
      timestamp: new Date(),
    }
    setTotalEvents((count) => count + 1)
    trackEvents(event)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 2</h1>
      <div style={{ marginBottom: '20px' }}>
        <button onClick={() => trackEvent('click', 'button-1')}>
          Track Button Click
        </button>
        <button
          onClick={() => trackEvent('hover', 'card')}
          style={{ marginLeft: '10px' }}
        >
          Track Hover Event
        </button>
        <button
          onClick={() => trackEvent('view', 'page')}
          style={{ marginLeft: '10px' }}
        >
          Track Page View
        </button>
        <button
          onClick={() => trackEvent('form', 'submit')}
          style={{ marginLeft: '10px' }}
        >
          Track Form Submit
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Events Created:</td>
            <td>{totalEvents}</td>
          </tr>
          <tr>
            <td>Events Sent:</td>
            <td>{events.length}</td>
          </tr>
          <tr>
            <td>Batches Processed:</td>
            <td>{batchesProcessed}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px' }}>
        <h3>Sent Analytics Events:</h3>
        <div
          style={{
            maxHeight: '200px',
            overflowY: 'auto',
            border: '1px solid #ccc',
            padding: '10px',
          }}
        >
          {events.length === 0 ? (
            <p style={{ color: '#666' }}>No events sent yet...</p>
          ) : (
            events.map((event, index) => (
              <div
                key={index}
                style={{ marginBottom: '5px', fontSize: '0.9em' }}
              >
                <strong>{event.timestamp.toLocaleTimeString()}</strong>:{' '}
                {event.type} - {event.target}
              </div>
            ))
          )}
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        Analytics events are batched - max 5 events or 3 second wait time
      </p>
    </div>
  )
}

interface ApiRequest {
  id: string
  data: any
}

function App3() {
  const [requests, setRequests] = useState<ApiRequest[]>([])
  const [totalRequests, setTotalRequests] = useState(0)
  const [processedRequests, setProcessedRequests] = useState<ApiRequest[]>([])

  // Create batched API request handler - Stable reference provided by useBatchedCallback
  const batchApiRequests = useBatchedCallback(
    (requests: ApiRequest[]) => {
      console.log('Processing batch of API requests:', requests)
      // Simulate API processing
      setProcessedRequests((current) => [...current, ...requests])
    },
    {
      maxSize: 4, // Process when 4 requests collected
      wait: 1500, // Or after 1.5 seconds
    },
  )

  function makeApiRequest(data: any) {
    const request: ApiRequest = {
      id: `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      data,
    }
    setTotalRequests((count) => count + 1)
    setRequests((current) => [...current, request])
    batchApiRequests(request)
  }

  return (
    <div>
      <h1>TanStack Pacer useBatchedCallback Example 3</h1>
      <div style={{ marginBottom: '20px' }}>
        <button
          onClick={() => makeApiRequest({ action: 'save', item: 'document' })}
        >
          Save Document
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'update', item: 'profile' })}
          style={{ marginLeft: '10px' }}
        >
          Update Profile
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'delete', item: 'file' })}
          style={{ marginLeft: '10px' }}
        >
          Delete File
        </button>
        <button
          onClick={() => makeApiRequest({ action: 'create', item: 'folder' })}
          style={{ marginLeft: '10px' }}
        >
          Create Folder
        </button>
      </div>

      <table>
        <tbody>
          <tr>
            <td>Total Requests Made:</td>
            <td>{totalRequests}</td>
          </tr>
          <tr>
            <td>Requests Queued:</td>
            <td>{requests.length - processedRequests.length}</td>
          </tr>
          <tr>
            <td>Requests Processed:</td>
            <td>{processedRequests.length}</td>
          </tr>
        </tbody>
      </table>

      <div style={{ marginTop: '20px', display: 'flex', gap: '20px' }}>
        <div style={{ flex: 1 }}>
          <h3>Queued Requests:</h3>
          <div
            style={{
              maxHeight: '150px',
              overflowY: 'auto',
              border: '1px solid #ccc',
              padding: '10px',
            }}
          >
            {requests.filter(
              (req) => !processedRequests.some((p) => p.id === req.id),
            ).length === 0 ? (
              <p style={{ color: '#666' }}>No requests queued...</p>
            ) : (
              requests
                .filter(
                  (req) => !processedRequests.some((p) => p.id === req.id),
                )
                .map((request) => (
                  <div
                    key={request.id}
                    style={{ marginBottom: '5px', fontSize: '0.9em' }}
                  >
                    {request.id}: {JSON.stringify(request.data)}
                  </div>
                ))
            )}
          </div>
        </div>

        <div style={{ flex: 1 }}>
          <h3>Processed Requests:</h3>
          <div
            style={{
              maxHeight: '150px',
              overflowY: 'auto',
              border: '1px solid #ccc',
              padding: '10px',
            }}
          >
            {processedRequests.length === 0 ? (
              <p style={{ color: '#666' }}>No requests processed yet...</p>
            ) : (
              processedRequests.map((request) => (
                <div
                  key={request.id}
                  style={{ marginBottom: '5px', fontSize: '0.9em' }}
                >
                  {request.id}: {JSON.stringify(request.data)}
                </div>
              ))
            )}
          </div>
        </div>
      </div>

      <p style={{ fontSize: '0.9em', color: '#666' }}>
        API requests are batched - max 4 requests or 1.5 second wait time
      </p>
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(
  <div>
    <App1 />
    <hr />
    <App2 />
    <hr />
    <App3 />
  </div>,
)
Our Partners
Unkey
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.

Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.