Documentation: refetch(asyncState)
Quick Start (30 seconds)
Re-run the last async operation with one function call:
const userData = asyncState(null);
// Load user data
await execute(userData, async (signal) => {
const response = await fetch('/api/user/123', { signal });
return response.json();
});
console.log(userData.data); // { name: 'John', ... }
// Later: refresh the data
await refetch(userData);
console.log(userData.data); // { name: 'John', email: 'updated@...' }That's it! The same fetch runs again without rewriting the function.
What is refetch()?
refetch() is a function that re-executes the last async operation you ran with execute().
Simply put: It's the refresh button for your data.
const posts = asyncState(null);
// First load
await execute(posts, async (signal) => {
return await fetch('/api/posts', { signal }).then(r => r.json());
});
// Refresh
await refetch(posts); // Runs the same fetch againSyntax
Shorthand (Recommended)
refetch(asyncState)Full Namespace
ReactiveUtils.refetch(asyncState)Parameters
asyncState- The async state that has a previous operation
Returns
- Promise that resolves to a result object (same as
execute())
Why Does This Exist?
The Challenge with Refreshing Data
When you want to refresh data, you typically have to repeat code:
Without refetch():
const userData = asyncState(null);
// First load
async function loadUser() {
await execute(userData, async (signal) => {
const response = await fetch('/api/user', { signal });
return response.json();
});
}
// Initial load
await loadUser();
// Later: refresh
await loadUser(); // Have to call the same function againOr store the function separately:
const userData = asyncState(null);
const userFetchFn = async (signal) => {
const response = await fetch('/api/user', { signal });
return response.json();
};
// First load
await execute(userData, userFetchFn);
// Later: refresh
await execute(userData, userFetchFn); // VerboseWhat's tedious about this?
❌ Duplicate function calls
❌ Or extra variables to store functions
❌ Repetitive code
❌ Easy to forget what to call
The Solution with refetch()
const userData = asyncState(null);
// First load
await execute(userData, async (signal) => {
const response = await fetch('/api/user', { signal });
return response.json();
});
// Later: refresh - just one line
await refetch(userData);What's better about this?
✅ No duplicate code
✅ No extra variables needed
✅ One simple call
✅ The function is remembered automatically
This method is especially useful when you need refresh buttons, pull-to-refresh, polling, or any scenario where data needs to reload.
Mental Model
Think of refetch() as the replay button for your async operation.
Without refetch() (Manual Replay)
Operation runs
↓
You save the operation instructions
↓
Later: You look up the instructions
↓
You run them manually againWith refetch() (One-Button Replay)
Operation runs → [Saved automatically]
↓
Later: Press [REFETCH]
↓
Same operation runs automaticallyKey Insight: The last operation is remembered for you—just hit replay.
How Does It Work?
When you call execute(), the function is saved:
// When you do this...
await execute(userData, async (signal) => {
return await fetch('/api/user', { signal }).then(r => r.json());
});
// Internally, the function is stored
userData.lastFn = async (signal) => {
return await fetch('/api/user', { signal }).then(r => r.json());
};When you call refetch():
Step 1: Check
↓
[Is there a lastFn stored?]
↓
Yes → Continue
No → Return error
↓
Step 2: Execute
↓
[Call execute(state, lastFn)]
↓
Step 3: Return Result
↓
[Same result as execute()]Behind the scenes:
// Simplified internal logic
function refetch(asyncState) {
if (asyncState.lastFn) {
return execute(asyncState, asyncState.lastFn);
} else {
return Promise.resolve({
success: false,
error: new Error('No function to refetch')
});
}
}Basic Usage
Example 1: Refresh Button
const products = asyncState(null);
// Load products
await execute(products, async (signal) => {
const response = await fetch('/api/products', { signal });
return response.json();
});
// User clicks "Refresh"
document.querySelector('#refreshBtn').addEventListener('click', async () => {
await refetch(products);
console.log('Products refreshed!');
});Example 2: Pull to Refresh
const feed = asyncState(null);
// Load feed
await execute(feed, async (signal) => {
const response = await fetch('/api/feed', { signal });
return response.json();
});
// Pull-to-refresh gesture
async function onPullToRefresh() {
console.log('Refreshing feed...');
await refetch(feed);
console.log('Feed refreshed!');
}Example 3: Polling/Auto-Refresh
const liveData = asyncState(null);
// Initial load
await execute(liveData, async (signal) => {
const response = await fetch('/api/live-data', { signal });
return response.json();
});
// Refresh every 5 seconds
setInterval(async () => {
await refetch(liveData);
console.log('Data updated');
}, 5000);Example 4: Retry After Error
const apiData = asyncState(null);
// Try to load
await execute(apiData, async (signal) => {
const response = await fetch('/api/data', { signal });
if (!response.ok) throw new Error('Failed');
return response.json();
});
// If error, show retry button
if (apiData.isError) {
document.querySelector('#retryBtn').addEventListener('click', async () => {
console.log('Retrying...');
await refetch(apiData);
});
}Deep Dive
What if There's No Function to Refetch?
If you call refetch() before ever calling execute(), it fails gracefully:
const data = asyncState(null);
// Try to refetch without executing first
const result = await refetch(data);
console.log(result);
// {
// success: false,
// error: Error('No function to refetch')
// }Safe handling:
const result = await refetch(data);
if (!result.success && result.error.message.includes('No function')) {
console.log('Nothing to refetch - load data first');
}refetch() vs New execute()
refetch() uses the saved function:
const data = asyncState(null);
await execute(data, async (signal) => {
return await fetch('/api/endpoint1', { signal }).then(r => r.json());
});
// This uses endpoint1 again
await refetch(data);New execute() replaces the saved function:
const data = asyncState(null);
await execute(data, async (signal) => {
return await fetch('/api/endpoint1', { signal }).then(r => r.json());
});
// This replaces endpoint1 with endpoint2
await execute(data, async (signal) => {
return await fetch('/api/endpoint2', { signal }).then(r => r.json());
});
// Now refetch uses endpoint2
await refetch(data);When to use refetch():
- User explicitly requests refresh
- You want to reload without changing the query
- Polling at intervals
- Retry after failure
When to use new execute():
- Different endpoint or parameters
- Different operation entirely
- First-time load
Return Value
refetch() returns the same result object as execute():
const data = asyncState(null);
await execute(data, async (signal) => {
return { value: 'data' };
});
const result = await refetch(data);
// Success case
console.log(result);
// {
// success: true,
// data: { value: 'data' }
// }
// Error case (if fetch fails)
// {
// success: false,
// error: Error(...)
// }Loading State During Refetch
Just like execute(), refetch() updates loading states:
const data = asyncState(null);
await execute(data, async (signal) => {
return await fetchData();
});
// Before refetch
console.log(data.loading); // false
// Start refetch
const promise = refetch(data);
console.log(data.loading); // true
// After refetch completes
await promise;
console.log(data.loading); // falseShow loading UI:
effect(() => {
if (data.loading) {
console.log('Refreshing...');
} else {
console.log('Refresh complete');
}
});
await refetch(data);Common Patterns
Pattern 1: Refresh with Cooldown
const data = asyncState(null);
let lastRefresh = 0;
async function refreshWithCooldown() {
const now = Date.now();
// Only allow refresh every 5 seconds
if (now - lastRefresh < 5000) {
console.log('Please wait before refreshing again');
return;
}
lastRefresh = now;
await refetch(data);
}Pattern 2: Conditional Refetch
const userData = asyncState(null);
// Only refetch if data is stale (older than 1 minute)
let lastFetch = Date.now();
async function refetchIfStale() {
const now = Date.now();
const oneMinute = 60 * 1000;
if (now - lastFetch > oneMinute) {
await refetch(userData);
lastFetch = now;
} else {
console.log('Data is still fresh');
}
}Pattern 3: Background Refresh
const notifications = asyncState(null);
// Initial load
await execute(notifications, async (signal) => {
const response = await fetch('/api/notifications', { signal });
return response.json();
});
// Silent background refresh every 30 seconds
setInterval(async () => {
// Refetch without showing loading UI
const oldData = notifications.data;
await refetch(notifications);
if (notifications.data.length > oldData.length) {
console.log('New notifications!');
}
}, 30000);Pattern 4: Refresh All
const user = asyncState(null);
const posts = asyncState(null);
const comments = asyncState(null);
// Load all data
await execute(user, async (signal) => fetchUser(signal));
await execute(posts, async (signal) => fetchPosts(signal));
await execute(comments, async (signal) => fetchComments(signal));
// Refresh everything at once
async function refreshAll() {
await Promise.all([
refetch(user),
refetch(posts),
refetch(comments)
]);
console.log('Everything refreshed!');
}Pattern 5: Smart Retry
const apiData = asyncState(null);
async function loadWithSmartRetry() {
await execute(apiData, async (signal) => {
return await fetchData(signal);
});
// Retry up to 3 times on failure
let retries = 0;
while (apiData.isError && retries < 3) {
retries++;
console.log(`Retry ${retries}/3...`);
await delay(1000 * retries); // Exponential backoff
await refetch(apiData);
}
if (apiData.isSuccess) {
console.log('Success after', retries, 'retries');
} else {
console.log('Failed after 3 retries');
}
}Summary
What is refetch()?
A function that re-runs the last async operation executed on an async state.
Key Features:
- ✅ One-line refresh
- ✅ Automatically uses last function
- ✅ No duplicate code needed
- ✅ Returns same result object as execute()
When to use it:
- Refresh buttons
- Pull-to-refresh
- Polling/auto-refresh
- Retry after errors
- Any time you need to reload data
Remember:
// First: execute saves the function
await execute(data, async (signal) => {
return await fetchData(signal);
});
// Later: refetch uses that saved function
await refetch(data);Common Pattern:
// Setup
const data = asyncState(null);
await execute(data, fetchFunction);
// In UI
<button onClick={() => refetch(data)}>
Refresh
</button>Related Methods:
execute()- Start async operation (saves function)abort()- Cancel current operationreset()- Clear all state