useWatchEffect
Auto-tracked reactive side effects that run immediately and clean up automatically
Why Choose useWatchEffect?
Perfect when you want reactive side effects but don't want to manually specify dependencies. Think of it as a "smart useEffect
" that knows which stores it depends on.
API Reference
Syntax
useWatchEffect(effect);
Parameters
effect
: Function containing side effect logic (can return cleanup function)
Returns
undefined
: No return value
Examples
Simple Reactive Side Effects
const userStore = createStore({
name: "John",
isOnline: false,
});
function DocumentTitleUpdater() {
useWatchEffect(() => {
// Automatically tracks userStore access
document.title = `${userStore.value.name} - ${
userStore.value.isOnline ? "Online" : "Offline"
}`;
});
return <div>Title updater active</div>;
}
No dependency array needed - ZenBox automatically detects that this effect depends on userStore
.
Auto-Save with Cleanup
const documentStore = createStore({
content: "",
autoSave: true,
saveInterval: 30000,
});
function AutoSaveEffect() {
useWatchEffect(() => {
// Conditional logic with early return
if (!documentStore.value.autoSave) return;
console.log(
`Setting up auto-save every ${documentStore.value.saveInterval}ms`
);
const interval = setInterval(() => {
console.log("Auto-saving content...");
api.saveDocument(documentStore.value.content);
}, documentStore.value.saveInterval);
// Cleanup runs automatically before next effect or on unmount
return () => {
console.log("Cleaning up auto-save interval");
clearInterval(interval);
};
});
return <div>Auto-save manager active</div>;
}
Smart dependency tracking: The effect automatically re-runs when autoSave
, saveInterval
, or content
changes.
Analytics Tracker
const analyticsStore = createStore({
userId: null,
sessionId: null,
pageViews: 0,
events: [],
});
function AnalyticsTracker() {
useWatchEffect(() => {
const { userId, sessionId, pageViews } = analyticsStore.value;
if (!userId || !sessionId) return;
// Track page view count
if (pageViews > 0) {
analytics.track("page_view_count", {
userId,
sessionId,
count: pageViews,
timestamp: Date.now(),
});
}
// Set up user identification
analytics.identify(userId, {
sessionId,
lastActive: Date.now(),
});
console.log(
`Analytics: Tracking user ${userId}, session ${sessionId}, ${pageViews} views`
);
});
return null;
}
useWatchEffect vs useWatch
Feature | useWatchEffect | useWatch |
---|---|---|
Dependency tracking | ✅ Automatic | ❌ Manual |
Initial execution | ✅ Immediate | ❌ On changes by default |
Configuration | ✅ Zero setup | ⚠️ Requires source param |
Best for | General side effects | Specific data watching |
useWatchEffect - When you want "reactive useEffect":
// Automatic dependency tracking
useWatchEffect(() => {
if (userStore.value.isLoggedIn) {
initializeUserSession(userStore.value.id);
}
});
useWatch - When you need precise control:
// Manual dependency specification
useWatch(
() => userStore.value.loginAttempts,
(attempts) => {
if (attempts > 3) {
lockAccount();
}
}
);
Best Practices
✅ Keep effects focused and pure
// Good: Single responsibility
useWatchEffect(() => {
document.title = `${appStore.value.title} - MyApp`;
});
✅ Use early returns for conditional logic
// Good: Clear conditional logic
useWatchEffect(() => {
if (!settingsStore.value.enableNotifications) return;
setupNotificationService();
return () => teardownNotificationService();
});
✅ Always return cleanup functions for resources
// Good: Proper resource management
useWatchEffect(() => {
const timer = setInterval(updateClock, 1000);
return () => clearInterval(timer);
});
❌ Don't modify reactive state inside effects
// Bad: Creates infinite loop
useWatchEffect(() => {
const count = counterStore.value.count;
counterStore.setState((state) => {
state.count = count + 1; // This triggers the effect again!
});
});
❌ Don't use for component rendering state
// Bad: Use useStoreValue instead
const [localState, setLocalState] = useState("");
useWatchEffect(() => {
setLocalState(store.value.data); // Unnecessary indirection
});
Hook Comparison
Hook | Dependency Tracking | Immediate | Re-renders | Use Case |
---|---|---|---|---|
useWatchEffect | ✅ Automatic | ✅ Yes | ❌ No | General reactive side effects |
useWatch | ❌ Manual | ❌ No | ❌ No | Specific data watching |
useEffect | ❌ Manual deps | ✅ Yes | ❌ No | React-specific effects |
useStoreValue | ❌ Manual | ✅ Yes | ✅ Yes | Component state synchronization |
Related Hooks
useWatch
- Manual dependency watching with callbacksuseStoreValue
- Component state synchronization with re-rendersuseComputed
- Derived reactive values for rendering