Client-Side useCallback¶
New in v2.0.5
Memoized callbacks keep listener references stable even when PhpSPA re-renders a target.
Keep event handlers and other functions stable between renders so you do not attach duplicate listeners every time PhpSPA updates a target. useCallback() memoizes a function until one of its dependencies changes.
- fn: The callback you plan to pass to an event listener, child script, or another hook.
- dependencies: Optional array containing state keys (strings) and/or direct values/objects. PhpSPA resolves each string against its state store and keeps the DOM node reference intact, so the memoized function refreshes whenever the underlying state value changes or one of the direct dependencies gains a new reference.
When to Use It¶
- Attaching DOM listeners once (e.g.,
addEventListener/removeEventListener). - Passing callbacks to custom elements or scripts that expect a stable reference.
- Pairing with
useEffect()cleanups that depend on referential equality.
Example: Toggling the Docs Navigation¶
const toggleNavLinks = () => {
const navToggle = document.querySelector('[data-nav-toggle]')
const navLinks = document.querySelector('[data-nav-links]')
if (!navToggle || !navLinks) return
const toggle = useCallback(() => {
navLinks.classList.toggle('hidden')
}, ['theme', navLinks])
navToggle.addEventListener('click', toggle)
}
'theme'refers to thethemekey inside PhpSPA state. WhensetState('theme', value)runs, the memoized function is recreated.navLinkskeeps the memoized function tied to the actual DOM node. If the layout swaps out the element, the dependency array detects the new reference and rebinds the handler automatically.
Cleanup Tip¶
Pair useCallback() with useEffect() when you need to tear down listeners:
useEffect(() => {
navToggle.addEventListener('click', toggle)
return () => navToggle.removeEventListener('click', toggle)
}, [toggle])
Because toggle stays stable until its dependencies change, the effect can safely remove and reattach the listener without leaking handlers.
Full Example with useCallback() and useEffect() cleanup¶
const toggleNavLinks = () => {
const navToggle = document.querySelector('[data-nav-toggle]')
const navLinks = document.querySelector('[data-nav-links]')
if (!navToggle || !navLinks) return
const toggle = useCallback(() => {
navLinks.classList.toggle('hidden')
}, ['theme', navLinks])
useEffect(() => {
navToggle.addEventListener('click', toggle)
return () => navToggle.removeEventListener('click', toggle)
}, [toggle])
}