proxy.has() - Reactive Storage Proxy Has Method
Quick Start (30 seconds)
const storage = reactiveStorage('localStorage', 'myApp');
// Check if a key exists
storage.set('username', 'Alice');
console.log(storage.has('username')); // true
// Use reactively - automatic tracking!
effect(() => {
if (storage.has('authToken')) {
console.log('User is logged in');
} else {
console.log('User is logged out');
}
});
storage.set('authToken', 'abc123'); // Logs: "User is logged in"
storage.remove('authToken'); // Logs: "User is logged out" ✨What just happened? You checked if data exists AND automatically tracked its presence - code updates when the key appears or disappears!
What is proxy.has()?
proxy.has() is a method that checks if a key exists in browser storage and automatically tracks that existence for reactivity.
Simply put: it's like checking if a file exists, but reactive. When you check inside an effect, your code will automatically re-run when that key is added or removed.
Think of it as a smart existence detector that not only tells you if something is there but also watches for when it appears or disappears.
Syntax
// Check if key exists
const exists = proxy.has(key)Parameters:
key(string) - The storage key to check
Returns:
boolean-trueif the key exists,falseif it doesn't
Why Does This Exist?
The Challenge with Regular Existence Checks
Here's the traditional way:
// Check if user is logged in
const hasToken = localStorage.getItem('authToken') !== null;
if (hasToken) {
showDashboard();
} else {
showLoginScreen();
}
// Later, user logs in
localStorage.setItem('authToken', 'abc123');
// Problem: UI doesn't update! ❌
// Still showing login screen
// hasToken variable still false
// Must manually check again and update UI
if (localStorage.getItem('authToken') !== null) {
showDashboard(); // Manual update required!
}What's the Real Issue?
Check if key exists
|
v
Store result in variable
|
v
Key added/removed from storage
|
v
[NOTHING HAPPENS] ❌
|
v
Variable still has old value
|
v
Must manually re-check everywhereProblems: ❌ Stale checks - Boolean result doesn't update when storage changes
❌ Manual re-checking - Must explicitly check again after every change
❌ Scattered logic - Existence checks duplicated throughout code
❌ Race conditions - Easy to miss storage changes between checks
The Solution with proxy.has()
const storage = reactiveStorage('localStorage', 'auth');
// Reactive existence check
effect(() => {
if (storage.has('authToken')) {
showDashboard();
} else {
showLoginScreen();
}
});
// Shows login screen initially
// User logs in
storage.set('authToken', 'abc123');
// Effect automatically re-runs!
// Dashboard shows! ✨
// User logs out
storage.remove('authToken');
// Effect automatically re-runs!
// Login screen shows! ✨What Just Happened?
storage.has('authToken') inside effect
|
v
Tracks key existence
|
v
storage.set('authToken', ...) called
|
v
Detects key now exists
|
v
Re-runs all effects watching existence
|
v
They call has() and get true
|
v
Update to show key exists! ✅Benefits: ✅ Automatic tracking - Existence changes trigger dependent code
✅ Always current - Check result always reflects current state
✅ Zero manual work - No need to re-check manually
✅ Centralized logic - One check, automatic everywhere
Mental Model
Think of regular existence checking as looking in a drawer once:
Regular localStorage check
┌─────────────────────┐
│ Open drawer │
│ │
│ Is item there? │
│ │
│ Yes/No │
│ │
│ [DONE] │
│ │
│ If item added/ │
│ removed later, │
│ you don't know │
└─────────────────────┘Now think of proxy.has() as installing a motion sensor:
proxy.has() (Smart Sensor)
┌─────────────────────┐
│ Check drawer │
│ │
│ Install sensor 📡 │
│ │
│ Item added? │
│ | │
│ v │
│ Alert! ✨ │
│ │
│ Item removed? │
│ | │
│ v │
│ Alert! ✨ │
└─────────────────────┘Key Insight: proxy.has() doesn't just check once - it monitors for when the key appears or disappears.
How Does It Work?
Let's look under the hood at what happens when you call proxy.has():
Step-by-Step Process
1️⃣ Check Storage
const exists = storage.has('authToken');First, it checks the actual browser storage:
Browser Storage (localStorage)
|
v
Look for key 'myApp:authToken'
|
v
Key found? → Return true
Key missing? → Return false2️⃣ Track Dependency (if in effect)
Is this call inside an effect?
|
YES | NO
| └──> Just return boolean
v
Track dependency on _keys
|
v
Store reference to current effect
|
v
Return boolean3️⃣ Automatic Re-run on Change
Later: storage.set() or remove() called
|
v
_keys set updated
|
v
Effects tracking _keys re-run
|
v
They call has() again
|
v
Get new existence status! ✨The Magic: Keys Tracking
Here's how has() tracks changes:
// Inside reactiveStorage()
const reactiveState = state({
_version: 0,
_keys: new Set(store.keys()) // Set of all current keys
});
// When you call has()
proxy.has = function(key) {
// ✨ This line tracks the _keys set!
const _ = reactiveState._keys;
// Now check if key exists
return actualStorage.has(key);
}
// When keys change
function notify() {
batch(() => {
reactiveState._version++;
reactiveState._keys = new Set(store.keys()); // ✨ Triggers watchers!
});
}What's happening?
has()reads_keysset to track it- When
set()orremove()changes keys,_keysupdates - All effects that read
_keysautomatically re-run - They call
has()again and see if key exists now
Basic Usage
Example 1: Simple Existence Check
const storage = reactiveStorage('localStorage', 'app');
// Set a value
storage.set('username', 'Alice');
// Check if it exists
console.log(storage.has('username')); // true
console.log(storage.has('email')); // false
// Remove it
storage.remove('username');
console.log(storage.has('username')); // falseExample 2: Check Before Reading
const storage = reactiveStorage('localStorage', 'app');
// Safe reading with existence check
if (storage.has('settings')) {
const settings = storage.get('settings');
console.log('Settings:', settings);
} else {
console.log('No settings found - using defaults');
}Example 3: Multiple Key Checks
const storage = reactiveStorage('localStorage', 'app');
storage.set('firstName', 'Alice');
storage.set('lastName', 'Johnson');
// Check multiple keys
const hasFirstName = storage.has('firstName');
const hasLastName = storage.has('lastName');
const hasEmail = storage.has('email');
console.log(hasFirstName); // true
console.log(hasLastName); // true
console.log(hasEmail); // falseReactive Existence Checks
Example 1: Show/Hide Based on Existence
const storage = reactiveStorage('localStorage', 'app');
// Show welcome banner only if user hasn't dismissed it
effect(() => {
const banner = document.getElementById('welcome-banner');
if (!storage.has('bannerDismissed')) {
banner.style.display = 'block';
} else {
banner.style.display = 'none';
}
});
// Dismiss button
document.getElementById('dismiss-banner').onclick = () => {
storage.set('bannerDismissed', true);
// Banner hides automatically! ✨
};Example 2: Authentication State
const storage = reactiveStorage('localStorage', 'auth');
// Reactive auth check
effect(() => {
const isLoggedIn = storage.has('authToken');
const loginBtn = document.getElementById('login-btn');
const logoutBtn = document.getElementById('logout-btn');
const userPanel = document.getElementById('user-panel');
if (isLoggedIn) {
loginBtn.style.display = 'none';
logoutBtn.style.display = 'block';
userPanel.style.display = 'block';
} else {
loginBtn.style.display = 'block';
logoutBtn.style.display = 'none';
userPanel.style.display = 'none';
}
});
// Login
function login(username, password) {
const token = authenticate(username, password);
storage.set('authToken', token);
// UI updates automatically! ✨
}
// Logout
function logout() {
storage.remove('authToken');
// UI updates automatically! ✨
}Example 3: Feature Flags
const storage = reactiveStorage('localStorage', 'features');
// Enable features based on flags
effect(() => {
const hasNewUI = storage.has('enableNewUI');
const hasDarkMode = storage.has('enableDarkMode');
const hasDebug = storage.has('enableDebug');
// Apply features
document.body.classList.toggle('new-ui', hasNewUI);
document.body.classList.toggle('dark-mode', hasDarkMode);
console.debug = hasDebug ? console.log : () => {};
});
// Toggle features
function toggleFeature(feature) {
if (storage.has(feature)) {
storage.remove(feature);
} else {
storage.set(feature, true);
}
// Features update automatically! ✨
}
toggleFeature('enableDarkMode');Example 4: Conditional Rendering
const storage = reactiveStorage('localStorage', 'app');
// Render different content based on existence
effect(() => {
const app = document.getElementById('app');
if (storage.has('userData')) {
const user = storage.get('userData');
app.innerHTML = `
<h1>Welcome back, ${user.name}!</h1>
<button onclick="clearUserData()">Logout</button>
`;
} else {
app.innerHTML = `
<h1>Welcome!</h1>
<button onclick="showLoginForm()">Login</button>
`;
}
});
function clearUserData() {
storage.remove('userData');
// UI switches to login automatically! ✨
}Real-World Examples
Example 1: First-Time User Experience
const storage = reactiveStorage('localStorage', 'app');
// Show tutorial only for new users
effect(() => {
if (!storage.has('tutorialCompleted')) {
showTutorial();
} else {
showMainApp();
}
});
function completeTutorial() {
storage.set('tutorialCompleted', true);
// Main app shows automatically! ✨
}
function resetTutorial() {
storage.remove('tutorialCompleted');
// Tutorial shows again! ✨
}Example 2: Shopping Cart Indicator
const storage = reactiveStorage('localStorage', 'shop');
// Show cart badge only when cart has items
effect(() => {
const badge = document.getElementById('cart-badge');
if (storage.has('cart')) {
const cart = storage.get('cart');
badge.textContent = cart.length;
badge.style.display = cart.length > 0 ? 'block' : 'none';
} else {
badge.style.display = 'none';
}
});
// Add to cart
function addToCart(item) {
const cart = storage.get('cart') || [];
cart.push(item);
storage.set('cart', cart);
// Badge appears/updates automatically! ✨
}
// Clear cart
function clearCart() {
storage.remove('cart');
// Badge disappears automatically! ✨
}Example 3: Draft Detection
const storage = reactiveStorage('localStorage', 'editor');
// Show "Continue Draft" button when draft exists
effect(() => {
const continueBtn = document.getElementById('continue-draft');
const newBtn = document.getElementById('new-document');
if (storage.has('documentDraft')) {
continueBtn.style.display = 'block';
continueBtn.onclick = () => loadDraft();
} else {
continueBtn.style.display = 'none';
}
});
function saveDraft(content) {
storage.set('documentDraft', {
content,
timestamp: Date.now()
});
// "Continue Draft" button appears! ✨
}
function publishDocument(content) {
// Save the published document
saveToServer(content);
// Clear draft
storage.remove('documentDraft');
// "Continue Draft" button disappears! ✨
}Example 4: Settings Initialization
const storage = reactiveStorage('localStorage', 'app');
// Initialize settings if they don't exist
effect(() => {
if (!storage.has('settings')) {
// Set defaults
storage.set('settings', {
theme: 'light',
language: 'en',
notifications: true
});
}
// Now use settings
const settings = storage.get('settings');
applySettings(settings);
});
// Reset to defaults
function resetSettings() {
storage.remove('settings');
// Effect runs and sets defaults again! ✨
}Example 5: Session Management
const storage = reactiveStorage('sessionStorage', 'app');
// Track active session
effect(() => {
const hasSession = storage.has('activeSession');
const sessionBar = document.getElementById('session-bar');
if (hasSession) {
const session = storage.get('activeSession');
sessionBar.innerHTML = `
Session active: ${session.username}
<button onclick="endSession()">End</button>
`;
sessionBar.style.display = 'block';
} else {
sessionBar.style.display = 'none';
}
});
function startSession(username) {
storage.set('activeSession', {
username,
startTime: Date.now()
});
// Session bar appears! ✨
}
function endSession() {
storage.remove('activeSession');
// Session bar disappears! ✨
}Common Patterns
Pattern 1: Check and Initialize
const storage = reactiveStorage('localStorage', 'app');
function ensureExists(key, defaultValue) {
if (!storage.has(key)) {
storage.set(key, defaultValue);
}
}
ensureExists('settings', { theme: 'light' });
ensureExists('favorites', []);Pattern 2: Toggle Pattern
const storage = reactiveStorage('localStorage', 'app');
function toggle(key, value = true) {
if (storage.has(key)) {
storage.remove(key);
return false;
} else {
storage.set(key, value);
return true;
}
}
toggle('darkMode'); // Turns on
toggle('darkMode'); // Turns offPattern 3: Required Keys Check
const storage = reactiveStorage('localStorage', 'app');
function hasRequiredData() {
const required = ['username', 'email', 'settings'];
return required.every(key => storage.has(key));
}
if (hasRequiredData()) {
console.log('All required data present');
} else {
console.log('Missing required data');
}Pattern 4: Conditional Defaults
const storage = reactiveStorage('localStorage', 'app');
function getOrDefault(key, defaultValue) {
return storage.has(key)
? storage.get(key)
: defaultValue;
}
const theme = getOrDefault('theme', 'light');
const lang = getOrDefault('language', 'en');Pattern 5: Batch Existence Check
const storage = reactiveStorage('localStorage', 'app');
function checkMultiple(keys) {
const results = {};
keys.forEach(key => {
results[key] = storage.has(key);
});
return results;
}
const status = checkMultiple(['user', 'settings', 'cache']);
console.log(status);
// { user: true, settings: true, cache: false }Pattern 6: Existence-Based Rendering
const storage = reactiveStorage('localStorage', 'app');
const components = {
onboarding: () => !storage.has('onboardingComplete'),
dashboard: () => storage.has('userData'),
settings: () => storage.has('preferences')
};
effect(() => {
const app = document.getElementById('app');
// Render first available component
for (const [name, shouldShow] of Object.entries(components)) {
if (shouldShow()) {
app.innerHTML = renderComponent(name);
return;
}
}
// Fallback
app.innerHTML = renderComponent('welcome');
});Summary
What is proxy.has()?
A method that checks if a key exists in browser storage and automatically tracks that existence for reactivity when used in effects.
Why use it?
- ✅ Automatic tracking of key existence
- ✅ Updates when keys are added or removed
- ✅ Simplifies conditional logic
- ✅ Perfect for feature flags and toggles
- ✅ Clean way to handle optional data
Key Takeaway:
Regular Existence Check proxy.has()
| |
Check once Check once
| |
Get boolean Track existence
| |
[DONE] Monitor changes
|
v
Auto-update! ✨One-Line Rule: proxy.has() = check if key exists + automatic tracking of appearance/disappearance.
When to use proxy.has():
- Checking authentication state
- Feature flag systems
- Showing/hiding optional UI elements
- Detecting first-time users
- Shopping cart presence detection
- Draft existence indicators
Remember: Check once, stay informed forever! 🎉