asyncState.isIdle
Computed property indicating idle state—no data, no error, not loading.
Quick Start (30 seconds)
const state = asyncState(null);
console.log(state.isIdle); // true (initial state)
execute(state, async () => {
console.log('During:', state.isIdle); // false (loading)
return 'data';
});
await new Promise(r => setTimeout(r, 100));
console.log(state.isIdle); // false (has data now)
reset(state);
console.log(state.isIdle); // true (back to idle)The magic: isIdle is true when nothing has happened yet—perfect for initial state UI!
What is asyncState.isIdle?
isIdle is a computed read-only property that returns true when the async state is in its initial idle state: no data, no error, and not loading.
Key points:
- Read-only computed property
- True when:
data === nullANDerror === nullANDloading === false - False otherwise
- Updates automatically when dependencies change
- Reactive—can be watched
Why Does This Exist?
Without isIdle
const state = asyncState(null);
// Must check three conditions for idle
if (state.data === null && state.error === null && !state.loading) {
console.log('Idle state');
}
// Easy to miss one condition
if (!state.data && !state.error) { // Wrong! Doesn't check loading
console.log('Might not be idle');
}Problem: Must check three negative conditions, easy to get wrong.
With isIdle
const state = asyncState(null);
// Single, clear check
if (state.isIdle) {
console.log('Idle state');
}Solution: One property that checks all conditions correctly.
Basic Usage
Example 1: Initial State
const state = asyncState(null);
console.log(state.isIdle); // true
console.log(state.data); // null
console.log(state.error); // null
console.log(state.loading); // false
// After execute, no longer idle
await execute(state, async () => 'data');
console.log(state.isIdle); // falseExample 2: UI States
const state = asyncState(null);
function render() {
if (state.isIdle) {
return '<button>Click to Load Data</button>';
}
if (state.loading) {
return '<div>Loading...</div>';
}
if (state.isError) {
return '<div>Error: ' + state.error.message + '</div>';
}
if (state.isSuccess) {
return '<div>Data: ' + state.data + '</div>';
}
}
console.log(render());
// '<button>Click to Load Data</button>'Example 3: First Load Detection
const state = asyncState(null);
watch(state, {
isIdle: (idle, wasIdle) => {
if (wasIdle && !idle) {
console.log('First load initiated!');
}
}
});
execute(state, async () => 'data');
// Logs: First load initiated!Common Patterns
Pattern 1: Onboarding UI
const dataState = asyncState(null);
function renderOnboarding() {
if (dataState.isIdle) {
return `
<div class="onboarding">
<h2>Welcome!</h2>
<p>Click below to get started</p>
<button onclick="loadData()">Get Started</button>
</div>
`;
}
// Already loaded or loading
return renderMainUI();
}Pattern 2: Empty State vs Idle
const state = asyncState(null);
function render() {
if (state.isIdle) {
// Never loaded
return '<div>Click to load data</div>';
}
if (state.isSuccess && state.data.length === 0) {
// Loaded but empty
return '<div>No items found</div>';
}
if (state.isSuccess) {
// Has data
return '<div>Items: ' + state.data.length + '</div>';
}
}Pattern 3: Lazy Loading
const imageState = asyncState(null);
function LazyImage({ src }) {
// Only load when scrolled into view
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && imageState.isIdle) {
console.log('Loading image...');
execute(imageState, async () => {
const response = await fetch(src);
const blob = await response.blob();
return URL.createObjectURL(blob);
});
}
});
// Observe image element
observer.observe(imageElement);
}Pattern 4: Multi-Step Flow
const step1State = asyncState(null);
const step2State = asyncState(null);
const step3State = asyncState(null);
function getCurrentStep() {
if (step1State.isIdle) return 1;
if (step2State.isIdle) return 2;
if (step3State.isIdle) return 3;
return 4; // All complete
}
function renderFlow() {
const step = getCurrentStep();
return `
<div>Step ${step} of 3</div>
${step === 1 ? '<button>Start</button>' : ''}
${step === 2 ? '<button>Continue</button>' : ''}
${step === 3 ? '<button>Finish</button>' : ''}
`;
}Understanding the Conditions
All Three Must Be True
const state = asyncState(null);
// Condition 1: data === null
console.log(state.data === null); // true ✓
// Condition 2: error === null
console.log(state.error === null); // true ✓
// Condition 3: loading === false
console.log(state.loading === false); // true ✓
// Result: isIdle
console.log(state.isIdle); // true ✓
await execute(state, async () => 'data');
console.log(state.data === null); // false (has data)
console.log(state.error === null); // true
console.log(state.loading === false); // true
console.log(state.isIdle); // false (no longer idle)State Transitions from Idle
const state = asyncState(null);
console.log('Initial:', state.isIdle); // true
// Start loading → leaves idle
execute(state, async () => {
console.log('Loading:', state.isIdle); // false
return 'data';
});
console.log('After start:', state.isIdle); // false (loading)
await new Promise(r => setTimeout(r, 100));
console.log('After complete:', state.isIdle); // false (has data)
// Only reset returns to idle
reset(state);
console.log('After reset:', state.isIdle); // trueEdge Cases
Gotcha 1: Initial Value Affects isIdle
// With null initial value
const state1 = asyncState(null);
console.log(state1.isIdle); // true
// With non-null initial value
const state2 = asyncState({ default: 'data' });
console.log(state2.isIdle); // false (has data)
console.log(state2.data); // { default: 'data' }
console.log(state2.loading); // false
console.log(state2.error); // null
// Not idle because data !== nullGotcha 2: Error Prevents Idle
const state = asyncState(null);
await execute(state, async () => {
throw new Error('Failed');
});
console.log(state.data); // null
console.log(state.error); // Error: Failed
console.log(state.loading); // false
console.log(state.isIdle); // false (has error)
// Only reset clears error and returns to idle
reset(state);
console.log(state.isIdle); // trueGotcha 3: Loading Prevents Idle
const state = asyncState(null);
execute(state, async () => {
console.log('Inside execute:');
console.log(' data:', state.data); // null
console.log(' error:', state.error); // null
console.log(' loading:', state.loading); // true
console.log(' isIdle:', state.isIdle); // false (loading)
return 'data';
});
console.log('Outside:', state.isIdle); // falseisIdle vs Other States
State Comparison
const state = asyncState(null);
// Initial: Idle
console.log({
isIdle: state.isIdle, // true
isSuccess: state.isSuccess, // false
isError: state.isError, // false
loading: state.loading // false
});
// After successful load
await execute(state, async () => 'data');
console.log({
isIdle: state.isIdle, // false
isSuccess: state.isSuccess, // true
isError: state.isError, // false
loading: state.loading // false
});
// After failed load
await execute(state, async () => {
throw new Error('Failed');
});
console.log({
isIdle: state.isIdle, // false
isSuccess: state.isSuccess, // false
isError: state.isError, // true
loading: state.loading // false
});Mutually Exclusive States
// Only ONE of these can be true at a time:
// - isIdle
// - loading
// - isSuccess
// - isError
const state = asyncState(null);
console.log(state.isIdle); // true
console.log(state.loading); // false
console.log(state.isSuccess); // false
console.log(state.isError); // falseWhen to Use
Use isIdle for:
- ✅ Initial/onboarding UI
- ✅ "Click to load" buttons
- ✅ Lazy loading detection
- ✅ Multi-step flow tracking
- ✅ Empty vs never-loaded distinction
Don't use for:
- ❌ Checking if loading (use
loading) - ❌ Checking success (use
isSuccess) - ❌ Checking error (use
isError) - ❌ Checking if has data (use
data !== null)
Summary
What it is: Computed property indicating idle state
Formula: data === null AND error === null AND loading === false
Returns: Boolean
Read-only: Yes (computed, cannot be set)
Reactive: Yes—updates when dependencies change
Use for: Initial-state UI and never-loaded detection
Quick Reference
// Create async state
const state = asyncState(null);
// Check idle
console.log(state.isIdle); // true (initial)
// After execute
await execute(state, async () => 'data');
console.log(state.isIdle); // false
// Return to idle
reset(state);
console.log(state.isIdle); // true
// Conditional UI
if (state.isIdle) {
return '<button>Load Data</button>';
}
// Watch idle changes
watch(state, {
isIdle: (idle) => {
if (!idle) {
console.log('No longer idle');
}
}
});One-Line Rule: isIdle is true when nothing has happened yet (no data, no error, not loading)—perfect for initial state UI.