Provides a callback that may cause side effects for the current component. The callback is evaluated only when the dependency array changes, and is called after rendering is complete.
Signature
export const useEffect = (cb, args = []) => {
const hook = getHook();
if (changed(hook.value, args)) {
hook.value = args;
hook.cb = cb;
}
};
Source: o.mjs:229
Parameters
callback
() => void | (() => void)
required
Callback function that executes side effects. Can optionally return a cleanup function that will be called before the component is removed from the DOM or before the effect runs again.Return value: Optional cleanup function
Array of callback dependencies. The effect only runs when values in this array change between renders.
- Empty array
[]: Effect runs only once after initial render
- No array: Effect runs after every render
- Array with values: Effect runs when any value changes
Returns
void (or optionally a cleanup function from the callback)
Timing
- The callback is called after rendering is complete
- This allows querying child DOM nodes safely
- Cleanup functions run before component removal or before the effect runs again
Examples
Window Resize Listener
import { x, useState, useEffect } from '@zserge/o';
const WindowWidth = () => {
const [width, setWidth] = useState(window.innerWidth);
function onResize() {
setWidth(window.innerWidth);
}
useEffect(() => {
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
return x`<div>Window width: ${width}</div>`;
};
Document Title Update
import { x, useState, useEffect } from '@zserge/o';
const PageTitle = ({ title }) => {
useEffect(() => {
document.title = title;
}, [title]);
return x`<h1>${title}</h1>`;
};
API Data Fetching
import { x, useState, useEffect } from '@zserge/o';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return x`<div>Loading...</div>`;
if (!user) return x`<div>User not found</div>`;
return x`
<div>
<h1>${user.name}</h1>
<p>${user.email}</p>
</div>
`;
};
Timer/Interval
import { x, useState, useEffect } from '@zserge/o';
const Timer = () => {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return x`<div>Seconds elapsed: ${seconds}</div>`;
};
DOM Manipulation
import { x, useEffect } from '@zserge/o';
const AutoFocus = () => {
let inputRef;
useEffect(() => {
// Access DOM after render
if (inputRef) {
inputRef.focus();
}
}, []);
return x`
<input
type="text"
ref=${(el) => inputRef = el}
placeholder="Auto-focused input"
/>
`;
};
Multiple Effects
import { x, useState, useEffect } from '@zserge/o';
const MultiEffect = ({ userId }) => {
const [user, setUser] = useState(null);
const [online, setOnline] = useState(false);
// Effect 1: Fetch user data
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setUser);
}, [userId]);
// Effect 2: Subscribe to online status
useEffect(() => {
const handleOnline = () => setOnline(true);
const handleOffline = () => setOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return x`
<div>
<p>User: ${user?.name || 'Loading...'}</p>
<p>Status: ${online ? 'Online' : 'Offline'}</p>
</div>
`;
};
Local Storage Sync
import { x, useState, useEffect } from '@zserge/o';
const PersistentCounter = () => {
const [count, setCount] = useState(() => {
const saved = localStorage.getItem('count');
return saved ? parseInt(saved) : 0;
});
useEffect(() => {
localStorage.setItem('count', count);
}, [count]);
return x`
<div>
<p>Count: ${count}</p>
<button onclick=${() => setCount(count + 1)}>Increment</button>
</div>
`;
};
Dependency Array Patterns
Run Once (Component Mount)
useEffect(() => {
console.log('Component mounted');
return () => console.log('Component unmounted');
}, []); // Empty array = run once
Run on Every Render
useEffect(() => {
console.log('Component rendered');
}); // No array = run every time
Run When Specific Values Change
useEffect(() => {
console.log('userId or status changed');
}, [userId, status]); // Run when userId or status changes
Important Notes
- Effects run after the DOM has been updated
- Cleanup functions run before re-running the effect or on component unmount
- Always include all values used inside the effect in the dependency array
- Don’t perform state updates directly in the effect body without conditions (infinite loop risk)
- Effects are useful for: subscriptions, timers, API calls, DOM manipulation, logging