asyncState.data
The property that holds the result data from successful async operations.
Quick Start (30 seconds)
const userState = asyncState(null);
console.log(userState.data); // null (initial)
await execute(userState, async () => {
const response = await fetch('/api/user');
return response.json();
});
console.log(userState.data); // { id: 1, name: 'Alice' }
// Access the data
const userName = userState.data.name; // 'Alice'The magic: data stores your async results—initially null, set automatically when operations succeed!
What is asyncState.data?
data is a reactive property on async state that contains the result of successful async operations. It's null initially and gets updated when execute() succeeds.
Key points:
- Read/write property (can be set manually)
- Starts as
nullor your initial value - Automatically updated by
execute() - Remains unchanged when operations fail
- Reactive—watchers and effects track it
Why Does This Exist?
The Problem Without Dedicated Data Property
// Manual data management
let userData = null;
let loading = false;
let error = null;
async function fetchUser() {
loading = true;
try {
const response = await fetch('/api/user');
userData = await response.json(); // Manually set
} catch (e) {
error = e;
} finally {
loading = false;
}
}Problems: Must manually track separate variables, easy to forget to update, no automatic reactivity.
The Solution
const userState = asyncState(null);
await execute(userState, async () => {
const response = await fetch('/api/user');
return response.json(); // Automatically sets data
});
console.log(userState.data); // Data available automaticallyBenefits: Automatic updates, reactive, centralized state, no manual tracking needed.
Basic Usage
Example 1: Reading Data
const state = asyncState(null);
await execute(state, async () => {
return { message: 'Hello!', count: 42 };
});
// Access data properties
console.log(state.data.message); // 'Hello!'
console.log(state.data.count); // 42Example 2: Initial Value
// Start with default data
const state = asyncState({ name: 'Guest', score: 0 });
console.log(state.data); // { name: 'Guest', score: 0 }
// After fetch, data is replaced
await execute(state, async () => {
return { name: 'Alice', score: 100 };
});
console.log(state.data); // { name: 'Alice', score: 100 }Example 3: Data Persists After Error
const state = asyncState(null);
// First successful fetch
await execute(state, async () => {
return { value: 'success' };
});
console.log(state.data); // { value: 'success' }
// Second fetch fails
await execute(state, async () => {
throw new Error('Failed!');
});
console.log(state.data); // { value: 'success' } (still there!)
console.log(state.error); // Error: Failed!Key insight: Failed operations don't clear data—old data remains accessible.
Example 4: Manual Updates
const state = asyncState(null);
// Set data manually
state.data = { manual: 'data' };
console.log(state.data); // { manual: 'data' }
// You can modify it directly
state.data.manual = 'updated';
console.log(state.data); // { manual: 'updated' }Watching Data Changes
const state = asyncState(null);
watch(state, {
data: (newData, oldData) => {
console.log('Data changed from', oldData, 'to', newData);
}
});
await execute(state, async () => {
return { id: 1, name: 'Alice' };
});
// Logs: Data changed from null to { id: 1, name: 'Alice' }Common Patterns
Pattern 1: Conditional Rendering
const postsState = asyncState(null);
function render() {
if (postsState.loading) {
return 'Loading posts...';
}
if (postsState.error) {
return 'Failed to load posts';
}
if (!postsState.data) {
return 'No posts yet';
}
return `Posts: ${postsState.data.length}`;
}Pattern 2: Data Transformation
const apiState = asyncState(null);
await execute(apiState, async () => {
const response = await fetch('/api/items');
const data = await response.json();
// Transform before storing
return {
items: data.items,
total: data.items.length,
timestamp: Date.now()
};
});
console.log(apiState.data.total); // Computed total
console.log(apiState.data.timestamp); // When fetchedPattern 3: Fallback with Computed
const state = asyncState(null);
// Add computed property using namespace method
computed(state, {
displayValue: function() {
return this.data || 'No data available';
}
});
console.log(state.displayValue); // 'No data available'
await execute(state, async () => 'Hello!');
console.log(state.displayValue); // 'Hello!'Pattern 4: Data Merging
const state = asyncState({ items: [] });
async function loadMore() {
await execute(state, async () => {
const response = await fetch('/api/items?page=2');
const newItems = await response.json();
// Merge with existing data
return {
items: [...state.data.items, ...newItems]
};
});
}Edge Cases
Gotcha 1: Data is Reactive
const state = asyncState({ count: 0 });
watch(state, {
data: () => console.log('Data changed!')
});
state.data = { count: 1 }; // Logs: Data changed!
state.data.count = 2; // No log (nested mutation)Solution: Reassign the entire object to trigger reactivity:
state.data = { ...state.data, count: 2 }; // Triggers watcherGotcha 2: Null vs Undefined
const state1 = asyncState(null);
console.log(state1.data); // null
const state2 = asyncState(undefined);
console.log(state2.data); // undefined
// Both are "falsy" but different
console.log(state1.data === null); // true
console.log(state2.data === undefined); // trueGotcha 3: Reference vs Value
const state = asyncState({ value: 1 });
const ref = state.data;
ref.value = 2;
console.log(state.data.value); // 2 (reference modified!)
// To avoid: use spread or clone
const copy = { ...state.data };
copy.value = 3;
console.log(state.data.value); // Still 2When to Use
Use data for:
- ✅ Storing API response data
- ✅ Caching fetched results
- ✅ Sharing data across components
- ✅ Conditional rendering based on data availability
Don't use for:
- ❌ Intermediate loading states (use
loading) - ❌ Error information (use
error) - ❌ Request tracking (use
requestId)
Summary
What it is: Reactive property holding async operation results
Initial value: null or provided initial value
Updated by: execute() on success, manual assignment
Type: Any (null, object, array, string, number, etc.)
Reactive: Yes—watchers and effects track changes
Read/write: Both (can read and modify)
Quick Reference
// Create with initial value
const state = asyncState({ default: 'value' });
// Read data
console.log(state.data);
// Set data manually
state.data = { new: 'value' };
// After execute
await execute(state, async () => {
return { fetched: 'data' };
});
console.log(state.data); // { fetched: 'data' }
// Check if has data
if (state.data !== null) {
console.log('Has data:', state.data);
}One-Line Rule: data holds your async results—initially null, automatically updated on success, and remains even after errors.