Back to Advanced Patterns

Store Event Handlers in Refs

advancedhooksrefsevent-handlersoptimization
28.0k🕒 2026-06-10Source ↗

Install this skill

npx skills add vercel-labs/agent-skills

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

React effects often trigger re-runs when passed function dependencies update on every render, leading to unnecessary event listener cleanup and re-attachment. By capturing event handlers in a mutable ref, you maintain a stable effect dependency while ensuring the logic executed inside the listener remains current. This pattern prevents performance bottlenecks caused by frequent DOM subscription churn. When updates to state or props pass new function references to a hook, the ref approach decouples the effect lifecycle from the function identity. This ensures listeners attached to global objects like the window or document stay persistent across re-renders, preventing memory leaks or race conditions. For environments supporting recent React versions, this technique acts as a manual implementation of event stability, keeping component side effects predictable and efficient.

When to Use This Skill

  • Creating custom hooks for global window or document events
  • Managing WebSocket message handlers that need access to fresh state
  • Preventing effect loops in components that frequently pass anonymous functions
  • Attaching scroll or resize listeners without constant re-binding

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • My window event listener re-runs on every render
  • How to keep event handlers stable in a useEffect
  • Prevent useEffect from constantly re-attaching listeners
  • Ref pattern for event handler synchronization
  • Stable callback pattern for global browser events

What this skill does

  • Decouples effect execution from handler reference changes
  • Maintains persistent event listeners across re-renders
  • Synchronizes latest handler logic with a stable effect dependency
  • Reduces redundant browser API event cleanup cycles
  • Provides a fallback for stable event logic without experimental APIs

When not to use it

  • When the handler logic does not require access to updated component state
  • When using React 19+ which natively supports event synchronization patterns
  • Simple components where re-subscription performance costs are negligible

Example workflow

  1. Initialize a useRef to hold the latest version of the handler function
  2. Create a layout effect or standard effect to sync the ref current value with the provided handler
  3. Define a secondary effect with an empty dependency array or specific trigger props
  4. Wrap the actual listener inside the effect to call the handler via the ref
  5. Return the cleanup function to remove the listener on component unmount

Prerequisites

  • Basic knowledge of React hooks lifecycle
  • Understanding of reference persistence with useRef

Pitfalls & limitations

  • !Forgetting to update the ref during renders leads to stale closure bugs
  • !Potential for minor race conditions if the ref is accessed after component unmount
  • !Overusing refs for logic that should be handled by dependency management

FAQ

Why not just put the handler in the dependency array?
Including a handler in the dependency array causes the effect to tear down and re-bind the listener every time the parent re-renders, which is inefficient for global events.
Does this require any special imports?
No, it uses standard React primitives like useRef and useEffect, making it compatible with most existing codebases.
Is this better than just using useCallback?
useCallback only stabilizes the reference if its dependencies are stable; if the handler depends on state that changes, the reference changes anyway, making this ref pattern more reliable.

How it compares

While manual management involves fragile dependency arrays, this ref-based approach guarantees that effects remain stable even when your handler references change dynamically.

Source & trust

28k stars🕒 Updated 2026-06-10
📄 Full skill instructions — original source: vercel-labs/agent-skills
## Store Event Handlers in Refs

Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.

**Incorrect (re-subscribes on every render):**

function useWindowEvent(event: string, handler: () => void) {
useEffect(() => {
window.addEventListener(event, handler)
return () => window.removeEventListener(event, handler)
}, [event, handler])
}


**Correct (stable subscription):**

function useWindowEvent(event: string, handler: () => void) {
const handlerRef = useRef(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])

useEffect(() => {
const listener = () => handlerRef.current()
window.addEventListener(event, listener)
return () => window.removeEventListener(event, listener)
}, [event])
}


**Alternative: use useEffectEvent if you're on latest React:**

import { useEffectEvent } from 'react'

function useWindowEvent(event: string, handler: () => void) {
const onEvent = useEffectEvent(handler)

useEffect(() => {
window.addEventListener(event, onEvent)
return () => window.removeEventListener(event, onEvent)
}, [event])
}


useEffectEvent provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/advanced-event-handler-refs/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/vercel-labs/agent-skills/advanced-event-handler-refs/SKILL.md
  • Cursor: ~/.cursor/skills/vercel-labs/agent-skills/advanced-event-handler-refs/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/vercel-labs/agent-skills/advanced-event-handler-refs/SKILL.md

🚀 Install with CLI:
npx skills add vercel-labs/agent-skills

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid advanced patterns issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Advanced Patterns and is published by Vercel Engineering, maintained in vercel-labs/agent-skills.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.