An Inertia page refreshes a job-status table with usePoll every two seconds; how do its options, router.reload and router.cancelAll keep requests under control?
answer
- usePoll(ms, reloadOptions, pollOptions)
- each tick is a router.reload
- mode: overlap, cancel or rest
- 90% throttle in background tabs
- cancelAll replaced cancel in v3
basics
~10 susePoll(2000, { only: ['jobs'] }) runs router.reload on each tick and stops on unmount. mode decides overlap, cancel or rest, background tabs are throttled unless keepAlive, and router.cancelAll() aborts in-flight requests.
solid answer
~50 s`usePoll(2000, { only: ['jobs'] })` from the React or Vue adapter calls `router.reload()` every two seconds and stops when the component unmounts; `reload` visits the current URL asynchronously with `preserveState` and `preserveScroll`, so the table updates without jumping. Pass `only` so each tick fetches just the polled prop. The third argument sets poll behaviour: `mode: 'overlap'` (default) fires even if the last request is still running, `'cancel'` aborts it, and `'rest'` waits the interval after each response, which stops a slow endpoint from piling up requests. Background tabs are throttled by 90% unless `keepAlive: true`, and `autoStart: false` with the returned `start`, `stop` and `polling` gives manual control. To abort everything in flight, for example before a destructive action, call `router.cancelAll()`, which in Inertia 3 replaced `router.cancel()` and cancels sync, async and prefetch requests unless told otherwise.
code
jsx · 23 linesimport { router, usePoll } from '@inertiajs/react'
export default function Jobs({ jobs }) {
const { start, stop, polling } = usePoll(
2000,
{ only: ['jobs'] },
{ mode: 'rest', keepAlive: false },
)
function purgeFailed() {
stop()
router.cancelAll({ prefetch: false })
router.delete('/jobs/failed', { onFinish: start })
}
return (
<section>
<button onClick={polling ? stop : start}>{polling ? 'Pause' : 'Resume'}</button>
<button onClick={purgeFailed}>Purge failed</button>
<JobTable rows={jobs} />
</section>
)
}go deeper
Recall that usePoll reloads the page's props on an interval and stops when the component unmounts.
Explain reload's preserve defaults, only for polled props, the three concurrency modes, background throttling and cancelAll's options.
Size intervals and modes against endpoint latency, rate limits and session writes, and pause polling around mutations.
Decide when polling should give way to broadcasting, weighing per-tab load against infrastructure complexity.
## The building block: `router.reload` `router.reload(options)` visits the **current URL** again. Compared with a plain `router.visit`, it: - runs as an **async** visit, so it does not cancel or block the user's own navigation; - sets `preserveState` and `preserveScroll` to `true`, so the table keeps its scroll position and local state; - accepts every visit option, most usefully `only` or `except` to fetch a subset of props. ## `usePoll` `usePoll(interval, requestOptions, pollOptions)` wraps `router.poll` in a hook: 1. `interval` is in milliseconds. 2. `requestOptions` are `router.reload` options, or a function returning them that is evaluated on every tick, so the request can include current component state. 3. `pollOptions` control scheduling: `keepAlive`, `autoStart` and `mode`. The hook starts on mount (unless `autoStart: false`), destroys the poll on unmount and returns `start`, `stop` and a `polling` flag, which says whether the poll is running, not whether a request is in flight. ## Concurrency modes | `mode` | Behaviour on each tick | Good for | |---|---|---| | `overlap` (default) | fires a new request even if the last is in flight | fast, cheap endpoints | | `cancel` | aborts the in-flight request, then fires | when only the newest answer matters | | `rest` | waits the interval after the previous request finishes | slow endpoints that must not stack up | With a job-status endpoint that sometimes takes three seconds, `overlap` at two seconds builds a queue of concurrent requests; `rest` turns the interval into a pause between responses. ## Background tabs When the tab is hidden, the poll is throttled by 90%, meaning only every tenth tick fires. Set `keepAlive: true` when the page must keep polling in the background, such as a monitor on a second screen. ## Cancelling: `router.cancelAll` `router.cancelAll()` aborts in-flight visits. By default it cancels **sync, async and prefetch** requests; pass `{ async: false, prefetch: false }` to cancel only the sync visit, or `{ prefetch: false }` to keep prefetches. It replaced v2's `router.cancel()`, which only cancelled the sync visit. For a single request, keep the token from `onCancelToken` and call `cancel()` on it; `onCancel` and `onFinish` fire. ## Server-side considerations Each tick is a full request through the Laravel stack: - **Only compute what is polled.** Wrap other props in closures so `only: ['jobs']` skips their queries. - **Session writes.** Frequent polls keep the session alive and write it on every request with the default database session driver; very short intervals add up. - **Rate limits.** If the route sits behind a throttle middleware, a two-second poll per open tab can hit the limit. - **Consider push.** When many users watch the same data, broadcasting over WebSockets may cost less than polling. ## Choosing an interval - Start from how fresh the data must be, not from how fast the server is. - Keep the interval longer than the endpoint's typical response time, or use `rest`. - Poll only the props that change, and make them closures on the server. - Stop polling while a modal edits the same data, so a tick does not overwrite what the user is looking at. ## Observing it In the network tab each tick is an Inertia request with `X-Inertia-Partial-Data: jobs` when `only` is set, and its JSON reply carries only that prop plus always props such as `errors`. With `mode: 'rest'` the gaps between requests stay constant even when a response is slow; with `overlap` you can see several requests in flight at once. Switching tabs away should drop the request rate to roughly one tenth. ## Version notes The `mode` option and function-valued request options arrived in client 3.2, and the returned `polling` flag in 3.7. `router.cancelAll()` with the `sync`, `async` and `prefetch` options is the v3 replacement for `router.cancel()`.
- Why does a polled table not jump to the top on every tick?Each tick is a `router.reload()`, and reload visits default to `preserveScroll: true` and `preserveState: true`. The page component keeps its instance and local state, only the returned props change, and Inertia skips its usual scroll reset. With `only: ['jobs']` the rest of the page's props are not even refetched.
- What did router.cancel() do in Inertia 2, and how do you reproduce it in 3?In v2, `router.cancel()` cancelled only the synchronous visit. In v3 it is gone and `router.cancelAll()` cancels synchronous, asynchronous and prefetch requests by default. `router.cancelAll({ async: false, prefetch: false })` reproduces the old behaviour.
saying these in an interview costs you the question
- usePoll keeps running after the component unmounts.
- Polling resets scroll position on every tick.
- router.cancel() is still the way to abort visits in Inertia 3.
- The default mode waits for the previous request before the next tick.
- The polling flag shows whether a request is in flight.