Refetching & staleness
Stale time decides when cached data needs revalidating; Refetch() is the explicit
override that always fetches.
Stale time#
Data is fresh for StaleTime after a successful fetch. A keyed
Execute that finds fresh data serves the cache and does not
fetch. The library default is TimeSpan.Zero — cached data renders instantly but
is always revalidated in the background on the next mount. "Cached" does not mean "no
request" unless you raise the stale time (this site uses 10 seconds).
RefetchOnMount#
Controls whether a keyed Execute that finds cached data also refetches:
| Value | Type | Description |
|---|---|---|
| IfStale | default | Refetch in the background only when the data is older than the stale time. |
| Always | | Always refetch in the background, even while fresh. |
| Never | | Serve the cache and never refetch on mount. Invalidation still refetches. |
A key with no cached data always fetches, regardless of this setting — and an
invalidated entry refetches even under Never: invalidation is an
explicit request, not a mount policy.
Manual Refetch()#
Refetch() re-runs the last Execute with a forced fresh fetch —
staleness, RefetchOnMount and Enabled are all bypassed. On the
cached path the result updates the shared entry, so every component on the key re-renders; a
concurrent same-key fetch is joined rather than duplicated. Before the first
Execute (or after disposal) it does nothing.
<button @onclick="() => _todos.Refetch()">Refresh</button>
// with one-shot overrides:
await _todos.Refetch(new RefetchParameters<List<Todo>>
{
StaleTime = TimeSpan.FromSeconds(30), // how soon the fresh result goes stale again
OnSettled = _ => _lastRefreshed = DateTimeOffset.Now,
});One-shot overrides#
RefetchParameters<T> can override callbacks, the token, and the stale/cache
times — for that call only. The key and fetch function always come from the last
Execute: changing those is a new query, not a refetch. A later bare
Refetch() is unaffected by earlier overrides.
Precedence of defaults#
Wherever stale time, cache time or RefetchOnMount can be set, the chain is:
- the per-execution value on
QueryParameters<T>, - else the longest matching prefix registered via
DejaClient.SetDefaults, - else the global
DejaOptionsdefault.
// Per-prefix defaults: reference data rarely changes, keep it fresh for an hour.
client.SetDefaults(QueryKey.Of("countries"), new QueryDefaults
{
StaleTime = TimeSpan.FromHours(1),
RefetchOnMount = RefetchOnMount.Never,
});Live demo#
- delectus aut autem
- quis ut nam facilis et officia qui
- fugiat veniam minus
A manual Refetch() always fetches — staleness, RefetchOnMount and
Enabled are bypassed. The existing list stays on screen while
IsReFetching is on: no loading flash. The override variant keeps the fresh result
“not stale” for 30 seconds instead of the site default of 10 — for this call only.
@inherits DejaComponentBase
@inject JsonPlaceholderApi Api
<div class="demo-toolbar">
<button class="demo-button primary" @onclick="() => _todos.Refetch()">Refetch()</button>
<button class="demo-button" @onclick="RefetchWithOverride">Refetch with StaleTime = 30 s</button>
</div>
<StateInspector Query="_todos" Label="List query"/>
@if (_todos.Data is { } todos)
{
<ul class="demo-list">
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
}
else if (_todos.IsLoading)
{
<p class="demo-note">Loading…</p>
}
@if (_lastRefetchAt is { } at)
{
<p class="demo-note">Last manual refetch settled at @at.ToString("HH:mm:ss").</p>
}
<p class="demo-note">
A manual <code>Refetch()</code> always fetches — staleness, <code>RefetchOnMount</code> and
<code>Enabled</code> are bypassed. The existing list stays on screen while
<code>IsReFetching</code> is on: no loading flash. The override variant keeps the fresh result
“not stale” for 30 seconds instead of the site default of 10 — for this call only.
</p>
@code {
private readonly Query<IReadOnlyList<TodoDto>> _todos = new();
private DateTimeOffset? _lastRefetchAt;
protected override Task OnInitializedAsync()
=> _todos.Execute(DocsKeys.TodoList(3), token => Api.GetTodosAsync(3, token), p => p.OnError = _ => { });
private Task RefetchWithOverride() => _todos.Refetch(new RefetchParameters<IReadOnlyList<TodoDto>>
{
StaleTime = TimeSpan.FromSeconds(30),
OnSettled = _ => _lastRefetchAt = DateTimeOffset.Now,
});
}